码迷,mamicode.com
首页 > 其他好文 > 详细

ThreadLocal

时间:2015-04-13 12:39:58      阅读:129      评论:0      收藏:0      [点我收藏+]

标签:

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。

 

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

  从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

  所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。

 

实现原理:ThreadLocal是如何做到为每一个线程维护变量的副本的呢?

      其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

      Thread 中有一个成员变量:ThreadLocal.Values localValues,通过在ThreadLocal中进行操作来维护Map变量:Map中元素的键为线程对象,而值对应线程的变量副本。

ThreadLocal:

 

技术分享
 1 /**
 2      * Sets the value of this variable for the current thread. If set to
 3      * {@code null}, the value will be set to null and the underlying entry will
 4      * still be present.
 5      *
 6      * @param value the new value of the variable for the caller thread.
 7      */
 8     public void set(T value) {
 9         Thread currentThread = Thread.currentThread();
10         Values values = values(currentThread);
11         if (values == null) {
12             values = initializeValues(currentThread);
13         }
14         values.put(this, value);
15     }
View Code

 

TreadLocal.Values:

技术分享
 /**
     * Per-thread map of ThreadLocal instances to values.
     */
    static class Values {

        /**
         * Size must always be a power of 2.
         */
        private static final int INITIAL_SIZE = 16;

        /**
         * Placeholder for deleted entries.
         */
        private static final Object TOMBSTONE = new Object();

        /**
         * Map entries. Contains alternating keys (ThreadLocal) and values.
         * The length is always a power of 2.
         */
        private Object[] table;

        /** Used to turn hashes into indices. */
        private int mask;

        /** Number of live entries. */
        private int size;

        /** Number of tombstones. */
        private int tombstones;

        /** Maximum number of live entries and tombstones. */
        private int maximumLoad;

        /** Points to the next cell to clean up. */
        private int clean;

        /**
         * Constructs a new, empty instance.
         */
        Values() {
            initializeTable(INITIAL_SIZE);
            this.size = 0;
            this.tombstones = 0;
        }

        ...

        /**
         * Adds an entry during rehashing. Compared to put(), this method
         * doesn‘t have to clean up, check for existing entries, account for
         * tombstones, etc.
         */
        void add(ThreadLocal<?> key, Object value) {
            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];
                if (k == null) {
                    table[index] = key.reference;
                    table[index + 1] = value;
                    return;
                }
            }
        }

        /**
         * Sets entry for given ThreadLocal to given value, creating an
         * entry if necessary.
         */
        void put(ThreadLocal<?> key, Object value) {
            cleanUp();

            // Keep track of first tombstone. That‘s where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;

            for (int index = key.hash & mask;; index = next(index)) {
                Object k = 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;
                    }

                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        }
    ...
}    
View Code

 

场景说明

在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。
 
如果一个对象要被多个线程访问,而该对象存在类变量被不同类方法读写,为获得线程安全,可以用ThreadLocal来替代类变量。

 

区别于优势

ThreadLocal和线程同步机制相比有什么优势呢?

 

同步机制:通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
ThreadLocal:从另一个角度来解决多线程的并发访问。在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
 

对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式:仅提供一份变量,让不同的线程排队访问

而ThreadLocal采用了“以空间换时间”的方式:为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

 

 

解决方法

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

 

 

 

http://baike.baidu.com/link?url=-MFpxUiVxQ9NTDK_LF7J6G5-OfMUJMJdNcJMFQTZ7kYB_V0AbL9q0E9huPUw4h7rwfIYpBj9ZhbXaILG5DQ9Jq

ThreadLocal

标签:

原文地址:http://www.cnblogs.com/luow/p/4387081.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!