ThreadLocal使用及应用场景分析

1. ThreadLocal 定义

  • 定义

    提供线程局部变量;一个线程线程局部变量在多个线程中,分别有独立的值(副本)。

  • 特点

    简单(开箱即用)、快速(无额外开销)、安全(线程安全)。

  • 场景

    多线程场景(资源持有、线程一致性、并发计算、线程安全等场景)。

  • 实现原理

    Java中用哈希表实现。

    ThreadLocal使用及应用场景分析_第1张图片

  • 应用范围

    几乎所有提供多线程特征的语言。

2. ThreadLocal 基本API

ThreadLocal使用及应用场景分析_第2张图片

public class ThreadLocalTest {
     public static ThreadLocal<Long> x = new ThreadLocal<Long>(){
         @Override
         protected Long initialValue() {
             System.out.println(Thread.currentThread().getId() + " Initial value run...");
             return Thread.currentThread().getId();
         }
     };

    public static void main(String[] args) {
        new Thread(()->{
            System.out.println(x.get());
        }).start();
        x.set(100L);
        //x.remove();
        System.out.println(x.get());
    }

}

3. ThreadLocal 的4种关键场景

  1. 线程资源持有

    持有线程资源供线程的各个部分使用,全局获取,减少编程难度。

    ThreadLocal使用及应用场景分析_第3张图片

  2. 线程资源一致性

    帮助需要保持线程一致的资源(如数据库事务)维护一致性,降低编程难度。

    ThreadLocal使用及应用场景分析_第4张图片

  3. 线程安全

    帮助只考虑了单线程的程序库,无缝向多线程场景迁移。

    ThreadLocal使用及应用场景分析_第5张图片

  4. 分布式计算

    帮助分布式计算场景的各个线程累积局部计算结果。

    ThreadLocal使用及应用场景分析_第6张图片

4. ThreadLocal 并发场景分析

  • Apache ab 压力并发测试工具的使用

    可参考:https://www.cnblogs.com/wtcl/p/6795740.html

  • 用synchronize实现线程安全的缺点

    性能得不到满足。

  • 用ThreadLocal实现并发线程安全

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class StatController {
        //static Integer c =0;
        static ThreadLocal<Integer> c = new ThreadLocal<Integer>(){
            @Override
            protected Integer initialValue(){
              return 0;
            }
        };
    
        void _add(){
            try {
                Thread.sleep(100);
                c.set(c.get() + 1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @RequestMapping("/stat")
        public Integer stat(){
            return c.get();
        }
    
        @RequestMapping("/add")
        public Integer add(){
            _add();
            return 1;
        }
    }
    
    

    上面的代码是有问题的,因为获取的值是每个线程自己的变量值,每次累加的也是每个线程自己的累加值。

    正确的写法应该是:

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashSet;
    
    @RestController
    public class StatController {
        static HashSet<Val<Integer>> set = new HashSet<Val<Integer>>();
        synchronized  static void addSet(Val<Integer> v){
            set.add(v);
        }
        static ThreadLocal<Val<Integer>> c = new ThreadLocal<Val<Integer>>(){
            @Override
            protected Val<Integer> initialValue(){
                Val<Integer> v = new Val<>();
                v.setV(0);
                addSet(v);
                return v;
            }
        };
    
        void _add(){
            try {
                Thread.sleep(100);
                Val<Integer> v = c.get();
                v.setV(v.getV() + 1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @RequestMapping("/stat")
        public Integer stat(){
            return set.stream().map(x-> x.getV()).reduce((a,x)-> a+x).get();
        }
    
        @RequestMapping("/add")
        public Integer add(){
            _add();
            return 1;
        }
    }
    
    
    public class Val<T> {
        T v;
    
        public T getV() {
            return v;
        }
    
        public void setV(T v) {
            this.v = v;
        }
    }
    

你可能感兴趣的:(并发编程,java,后端)