美文网首页
多线程安全之ThreadLocal

多线程安全之ThreadLocal

作者: 一个OUT的人 | 来源:发表于2018-03-10 15:40 被阅读0次

    使用场景

    每个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从当前线程同步给子线程。

    相关文章

      网友评论

          本文标题:多线程安全之ThreadLocal

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