zoukankan      html  css  js  c++  java
  • ThreadLocal:

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

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

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

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

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

     

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

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

          Thread 中有一个成员变量:ThreadLocal.Values localValues,通过在ThreadLocal中进行操作来维护此变量:private Entry[] table 数组

    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

    仔细可能过源码后,可以知道,ThreadLocal其实是实现了一个线程本地变量的存储,每个thread都拥有一个相同名称的局部变量,而所有的thread都共享同一个ThreadLocal对象,

    因此可知,ThreadLocal并不是用来实现所谓的多线程并发问题的,因为每个thread都只能访问自己的局部变量,根本访问不了其他thread的变量,也不需要访问。因此,也不需要考虑thread之间的同步问题

    ThreadLocal源码中的注释:

    /**
     * Implements a thread-local storage, that is, a variable for which each thread
     * has its own value. All threads share the same {@code ThreadLocal} object,
     * but each sees a different value when accessing it, and changes made by one
     * thread do not affect the other threads. The implementation supports
     * {@code null} values.
     *
     * @see java.lang.Thread
     * @author Bob Lee
     */
  • 相关阅读:
    [翻译]windows下 连接到 bitnami的phpmyadmin
    Redmine 和GitBlit仓库服务器整合
    Xshell出现要继续使用此程序必须应用到最新的更新或使用新版本
    Codeigniter 列出所有控制器和控制器的方法(类似路由列表)
    FastStone Capture 文件名设置小记录
    [转]sourceforge文件下载过慢
    Cacti 添加 CPU 监听
    Cacti 发送警告邮件
    尝试让Virtualbox的Ubuntu可以调整分辨率
    Html5+离线打包创建本地消息
  • 原文地址:https://www.cnblogs.com/luow/p/4387081.html
Copyright © 2011-2022 走看看