java中的ThreadLocal-learning

为什么需要ThreadLocal

多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。

而线程安全是指

当多个线程访问某个方法时,不管你通过怎样的调用方式或者说这些线程如何交替的执行,我们在主程序中不需要去做任何的同步,

这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类时线程安全的。

或者:

不同的线程可以访问相同的资源,而不会暴露出错误的行为或产生不可预知的结果。

 下面是线程不安全,相互影响:

public class UsuallyThreadTest {
    static  String localName = null;

    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                localName ="quan";
                System.out.println(localName+"  "+Thread.currentThread().getName());
                localName = null;
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                System.out.println(localName+" after set localName= null "+Thread.currentThread().getName());
            }
        });

        System.getenv();
//        System.getProperty()

        thread1.start();
        thread2.start();
    }
    /**
     * quan  Thread-0
     * quan after set localName= null Thread-1
     *
     * 线程1的修改对线程2产生影响,不安全。
     */
}

 

ThreadLocal是什么

它提供线程本地变量,如果创建一乐ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,

在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。

 

ThreadLocal例子

public class LearningforThreadlocal {

    static  ThreadLocal<String> localName = new ThreadLocal<String>();

    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                localName.set("quan");
                System.out.println(localName.get());
            }
        });


        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                System.out.println(localName.get()+"--->"+"after thread1 ,noremove");
                localName.set("zhi");
                System.out.println(localName.get());
                localName.remove();
                System.out.println("remove......::"+localName.get()+" in "+Thread.currentThread().getName());

            }
        });

//        Thread thread3 = new Thread(new Runnable() {
//            public void run() {
//                System.out.println("no use set   "+localName.get()+" in "+Thread.currentThread().getName());
//            }
//        });

        Thread thread3 = new Thread(()->{
            System.out.println("no use set   "+localName.get()+" in "+Thread.currentThread().getName());
        });

        thread1.start();
        thread2.start();
        thread3.start();
}

}


    /**
     * re:
     * quan
     * no use set   null in Thread-2
     * null--->after thread1 ,noremove
     * zhi
     * remove......::null in Thread-1
     */

从上面可以看出,其实两个线程是不会相互影响的!!!!

ThreadLocal解析

set方法解析

/**
     * set方法解析:
     *     public void set(T value) {
     *         Thread t = Thread.currentThread();//获取当前线程
     *         ThreadLocalMap map = getMap(t);//getMap方法如下面所示
     *         if (map != null)//如果不为空直接将当前线程中的threadLocals设置为键为当前ThreadLocal的引用,值为value
     *             map.set(this, value);
     *         else
     *             createMap(t, value);//否则创建threadLocals并设置键值对,createMap如下:
     *     }
     *
     *   ThreadLocalMap getMap(Thread t) {
     *         return t.threadLocals;//返回当前线程中的变量threadLocals,第一次使用set方法是为null
     *     }
     *
     *    void createMap(Thread t, T firstValue) {
     *         t.threadLocals = new ThreadLocalMap(this, firstValue);这里的createMap不仅建立了threadLocals,而且还设置了值
     *     }
     */

 

Get方法解析

/**
     * get() 解析:
     *     public T get() {
     *         Thread t = Thread.currentThread();获取当前线程
     *         ThreadLocalMap map = getMap(t); 获取当前线ThreadLocals集合
     *         if (map != null) {
     *             ThreadLocalMap.Entry e = map.getEntry(this);
     *             //不为空直接获取集合中的值,this指的是哪个 ThreadLocal(例子localName)调用的引用
     *             if (e != null) {
     *                 @SuppressWarnings("unchecked")
     *                 T result = (T)e.value;获取ThreadLocal(例子localName)对应的值
     *                 return result;
     *             }
     *         }
     *         return setInitialValue();
     *     }
     *
     *    private T setInitialValue() {
     *         T value = initialValue();//内部方法,返回null
     *         Thread t = Thread.currentThread();
     *         ThreadLocalMap map = getMap(t);
     *         if (map != null)
     *             map.set(this, value);
     *         else
     *             createMap(t, value);
     *         return value;返回null
     *     }
     *
     */

 

ThreadLocal和Thread的关系

/**
 * 进入Thread内部可以知道有两个属性:
 * ThreadLocal.ThreadLocalMap threadLocals = null;
 * ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
 * 由前缀知道都是ThreadLocal内部类ThreadLocalMap类型的变量
 *
 * ThreadLocal类型的本地变量是存放在具体的线程空间上,set方法将value添加到调用线程的threadLocals中,
 * 当调用线程调用get方法时候能够从它的threadLocals中取出变量。
 * 线程不断,变量就一直在threadlocals中,所以不用的时候使用remove
 *

 

ThreadLocal无继承性

例子说明:

/**
 * ThreadLocal不具有继承性
 */
public class ThreadLocalInherit {
    static ThreadLocal<String> localName = new ThreadLocal<String>();

    public static void main(String[] args) {
        localName.set("DONE");//在main线程里面设置了localName的值
        System.out.println(Thread.currentThread().getName()+"使用了:localName.set(\"DONE\")");

        Thread thread = new Thread(new Runnable() {
            public void run() {
                System.out.println("子类"+localName.get());
            }//子类去获取的时候
        });

        thread.start();
    }
    /**
     * re:
     * main使用了:localName.set("DONE")
     * 子类null
     */

}

注意:InheritableThreadLocal类继承了ThreadLocal,扩展了ThreadLocal。实现了可以继承的关系

 

ThreadLocalMap与内存泄漏

/**
 * 解析一下ThreadLocals的类型ThreadLocalMap
 * 实际上是一个:private Entry[] table;
 * Entry[] 是:
 *
 * static class Entry extends WeakReference<ThreadLocal<?>> {
 *   Object value;
 *   Entry(ThreadLocal<?> k, Object v) {构造函数,ThreadLocal的引用k被传进WeakReference的构造函数。
 *        super(k);
 *        value = v;
 *             }
 *         }
 *---》所以ThreadLocalMap的key就是对ThreadLocal的弱引用
 *
 * 如果没有执行remove,有可能造成内存泄漏
 *
 * key弱引用在gc的时候没有其他强依赖,就会被gc掉。但是value不会。
 * 可能会出现key为null,但是value还存在的情况。
 * 所以我们使用后一定要用remove掉
 */

ThreadLocal只是一个工具类,他为用户提供get、set、remove接口操作实际存放本地变量的threadLocals(调用线程的成员变量),

threadLocals是一个ThreadLocalMap类型的变量,

 

posted @ 2020-09-09 23:17  小丑quan  阅读(142)  评论(0)    收藏  举报