美文网首页
ThreadLocal

ThreadLocal

作者: 少博先生 | 来源:发表于2018-02-01 16:37 被阅读0次

    ThreadLocal,顾名思义,这是本地线程的意思,不好意思,这次顾错了。
    ThreadLocal类是用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程的上下文。
    按照我的理解,ThreadLocal就像是一个存储线程公共值的map,key是当前线程,value是这个公共值,各个线程之间对该公共值的使用互不影响,相互独立。

    一、案例引入

    举一个例子来说明一下,如每个人刚出生都是0岁,假设每个人能活100年,那么他去世的年龄就是100岁。

    1、(不使用ThreadLocal情况下)

    先定义一个Person类:

    package com.bxw.concurrent.threadLocal;
    
    public class Person {
        private static int age = 0;
    
        public void grow(){
            age++;
        }
        public int getAge(){
            return age;
        }
    }
    

    再定义一个PersonThread类:

    package com.bxw.concurrent.threadLocal;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class PersonThread extends Thread{
        private Person person;
    
        PersonThread(Person person){
            this.person = person;
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"的出生年龄是" + person.getAge());
            for(int i = 0; i < 100; i++){
                person.grow();
            }
            System.out.println(Thread.currentThread().getName()+"的死亡年龄是" + person.getAge());
        }
    
        public static void main(String[] args) {
            Person person = new Person();
            ExecutorService executorService = Executors.newFixedThreadPool(4);
            for(int i = 0; i < 4; i++){
                executorService.submit(new PersonThread(person));
            }
            executorService.shutdown();
        }
    }
    
    
    运行结果:

    可以看到四个线程的死亡年龄分别是100,200,300,400。这就说明了这四个线程共享了年龄这个静态变量,线程安全出了问题。
    那么如何让每个线程都独享一个静态变量呢,看看下面对Person的改造

    2、(使用ThreadLocal情况下)
    package com.bxw.concurrent.threadLocal;
    
    public class Person {
        private static int age = 0;
        
        private static MyThreadLocal<Integer> ageLocal = new MyThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return 0;
            }
        };
    
        public void grow(){
            ageLocal.set(ageLocal.get() + 1);
        }
        public int getAge(){
            return ageLocal.get();
        }
    }
    

    PersonThread不用改变,再次运行,运行结果:

    这时,每个线程的出生年龄都是0,死亡年龄都是100,说明每个线程都拥有自己的静态变量,相互独立,不相互影响。

    调整下PersonThread中线程池线程个数
    ExecutorService executorService = Executors.newFixedThreadPool(4);
    再次运行,可以看到线程三,活了两个100年,那么线程三死亡的时候年龄是200,再次说明这个静态变量被单个线程独立使用。

    三、ThreadLocal方法介绍

    public void set(T value):将值放入线程局部变量中

    public T get():从线程局部变量中获取值

    public void remove():从线程局部变量中移除值

    protected T initialValue():线程局部变量初始值(initialValue时protected的,就是要提醒这个方法要重写)

    四、实现穷人版ThreadLocal

    了解了ThreadLocal的基本原理,可以仿照ThreadLocal写个简易版的ThreadLocal

    package com.bxw.concurrent.threadLocal;
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    public class MyThreadLocal<T> {
        private Map<Thread, T> container = Collections.synchronizedMap(new HashMap<Thread, T>());
    
        public void set(T value) {
            container.put(Thread.currentThread(), value);
        }
    
        public T get() {
            Thread thread = Thread.currentThread();
            T value = container.get(thread);
            if (value == null && !container.containsKey(thread)) {
                value = initialValue();
                container.put(thread, value);
            }
            return value;
        }
    
        public void remove() {
            container.remove(Thread.currentThread());
        }
    
        protected T initialValue() {
            return null;
        }
    }
    

    将PersonThread中的ThreadLocal替换成MyThreadLocal,

    private static MyThreadLocal<Integer> ageLocal = new MyThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return 0;
            }
    };
    

    运行结果与ThreadLocal一模一样。。。

    五、JDK中使用ThreadLocal的类(比如BigDecimal)

    BigDecimal中有一个静态内部类StringBuilderHelper,BigDecimal中使用到了ThreadLocal设置了初始值(一个单独的StringBuilderHelper对象),就可以实现让BigDecimal在多线程环境下,每个线程都拥有一个StringBuilderHelper对象。

    六、总结

    当一个类中的变量(无论是否静态)在并发条件下需要考虑线程安全,就需要使用ThreadLocal,让ThreadLocal保证每个线程都独立拥有自己的变量。

    相关文章

      网友评论

          本文标题:ThreadLocal

          本文链接:https://www.haomeiwen.com/subject/wsblzxtx.html