多线程安全之ThreadLocal

2018-03-10  本文已影响0人  一个OUT的人

使用场景

每个ThreadLocal可以放一个线程级别的变量,但是它本身可以被多个线程共享使用,而且又可以达到线程安全的目的,且绝对线程安全。

Demo

public class ThreadLocalDemo {

    static class ResourceClass {

       public static ThreadLocal sTHREADLOCAL_1 = new ThreadLocal();

        public static String sNoThreadLocal;

    }

    static classThreadLocalSetClass {

       public void setOne(String value) {

           ResourceClass.sTHREADLOCAL_1.set(value);

       }

       public void setNoThreadLocalValue(String value) {

            ResourceClass.sNoThreadLocal = value;

       }

    }

    static classThreadLocalGetClass {

       public void display() {

           System.out.println(Thread.currentThread().getName() +": ThreadLocal "

                    + ResourceClass.sTHREADLOCAL_1.get() + ", NOThreadLocal "

                    + ResourceClass.sNoThreadLocal);

       }

    }

    public static void main(String[] args) {

       final ThreadLocalSetClass threadLocalSetClass = new ThreadLocalSetClass();

       final ThreadLocalGetClass threadLocalGetClass = new ThreadLocalGetClass();

       for (int i = 0; i < 15; i++) {

           final String resouce1 = "Vlaue = (" + i + ")";

           Threadthread = new Thread() {

                public void run() {

                    try {

                        threadLocalSetClass.setOne(resouce1);

                        threadLocalSetClass.setNoThreadLocalValue(resouce1);

                        threadLocalGetClass.display();

                    }finally {

                        ResourceClass.sTHREADLOCAL_1.remove();

                    }

                }

           };

           thread.setName("Thread - " + i);

           thread.start();

       }

    }

}

输出结果

Thread - 11: ThreadLocal Vlaue = (11),NOThreadLocal Vlaue = (12)

Thread - 10: ThreadLocal Vlaue = (10),NOThreadLocal Vlaue = (12)

Thread - 9: ThreadLocal Vlaue = (9),NOThreadLocal Vlaue = (1)

Thread - 1: ThreadLocal Vlaue = (1),NOThreadLocal Vlaue = (1)

Thread - 3: ThreadLocal Vlaue = (3),NOThreadLocal Vlaue = (2)

Thread - 2: ThreadLocal Vlaue = (2),NOThreadLocal Vlaue = (1)

Thread - 4: ThreadLocal Vlaue = (4),NOThreadLocal Vlaue = (4)

Thread - 5: ThreadLocal Vlaue = (5),NOThreadLocal Vlaue = (4)

Thread - 6: ThreadLocal Vlaue = (6),NOThreadLocal Vlaue = (6)

Thread - 14: ThreadLocal Vlaue = (14),NOThreadLocal Vlaue = (14)

Thread - 13: ThreadLocal Vlaue = (13),NOThreadLocal Vlaue = (14)

Thread - 0: ThreadLocal Vlaue = (0),NOThreadLocal Vlaue = (1)

Thread - 8: ThreadLocal Vlaue = (8),NOThreadLocal Vlaue = (1)

Thread - 7: ThreadLocal Vlaue = (7),NOThreadLocal Vlaue = (12)

Thread - 12: ThreadLocal Vlaue = (12),NOThreadLocal Vlaue = (12)

注意:根据Thread的启动时间,每次打印出的线程顺序会有不同

结果分析

使用了ThreadLocal定义的变量每次使用的i值和线程命名时所使用的i值一样,而不使用ThreadLocal定义的变量则i值不确定。可验证ThreadLocal做到了线程安全。

原理分析

针对Android 6.0的ThreadLocal进行分析:

示例中使用了set(),get(),remove()方法,接下来针对set()进行分析:

    public void set(Tvalue){

       Thread currentThread=Thread.currentThread();

       Values values=values(currentThread);

       if (values==null) {

           values=initializeValues(currentThread);

       }

       values.put(this,value);

}

Values values(Thread current){

       return current.localValues;

    }

/**

         * Sets entry forgiven ThreadLocal to given value, creating an

         * entry ifnecessary.

         */

        void put(Thread Localkey,Object value){

            cleanUp();

            // Keep track offirst tombstone. That's where we want to go back

            // and add anentry if necessary.

            int firstTombstone= -1;

            for (int index=key.hash&mask;;index=next(index)){

                Objectk =table[index];

                if (k ==key.reference){

                    //Replace existing entry.

                    table[index+1] =value;

                    return;

                }

                if (k ==null) {

                    if (firstTombstone== -1) {

                        //Fill in null slot.

                        table[index]=key.reference;

                        table[index+1] =value;

                        size++;

                        return;

                    }

                    // Goback and replace first tombstone.

                    table[firstTombstone]=key.reference;

                    table[firstTombstone+ 1] =value;

                    tombstones--;

                    size++;

                    return;

                }

                // Rememberfirst tombstone.

                if (firstTombstone== -1 && k ==TOMBSTONE){

                    firstTombstone=index;

                }

            }

        }

File: libcore/luni/src/main/java/java/lang/ThreadLocal.java

可简单理解为:每个Thread对象都有一个ThreadLocal.Vlaues的对像。而ThreadLocal就是对通过每个Thread自身的ThreadLocal.Vlaues对像进行值的保存和读取。相当于每个线程有自己的数据。所以可以做到线程安全。

延生

InheritableThreadLocal

扩展了 ThreadLocal,为子线程提供从父线程那里继承的值。

protected T childValue(T parentValue){

        return parentValue;

    }

    @Override

    Valuesvalues(Thread current){

        return current.inheritableValues;

    }

    @Override

    ValuesinitializeValues(Threadcurrent){

        return current.inheritableValues= new Values();

    }

File:libcore/luni/src/main/java/java/lang/InheritableThreadLocal.java

InheritableThreadLocal.java这个类只是重写了ThreadLocal的几个方法,那他是怎么做到继承父线程的值呢?重点就在Thread.java是怎么使用inheritableValues这个变量了。

ThreadLocal.Values inheritableValues;

public Thread() {

        create(null,null,null,0);

    }

private void create(ThreadGroup group,Runnable runnable,String threadName,long stackSize){

if(currentThread.inheritableValues!=null){

            inheritableValues=new ThreadLocal.Values(currentThread.inheritableValues);

        }

}

File:libcore/libart/src/main/java/java/lang/Thread.java

在Thread的创建时,会把inheritableValues从当前线程同步给子线程。

上一篇下一篇

猜你喜欢

热点阅读