ThreadLocal的使用及实现

来源:互联网 发布:网络退票手续费发票 编辑:程序博客网 时间:2024/05/08 18:36

在java中,如果一个变量需要被多个线程访问,可以使用volatile来声明它为“易变的”。而假如一个变量要被持有它的某个线程独享,在java中,它可以使用java.lang.ThreadLocal类来实现线程本地存储的功能。这样,数据便只在同一个线程内可见和共享,因此,即便不同步也能保证线程之间不出现数据争用。

ThreadLocal使得各线程能够保持各自独立的一个对象,通过ThreadLocal.set()来设置对象的值,保存在每个线程自己都有的一个map对象当中,每个ThreadLocal对象会有一个线程范围内唯一hashcode作为key,ThreadLocal.get()内部通过这个key从map中取值,因此取出来的是各自自己线程中的对象,ThreadLocal实例事实上只是作为map的key来使用的。


一个ThreadLocal的例子:

 

Java代码  收藏代码
  1. package com.threadlocal.test;  
  2.   
  3. /** 
  4.  * @Author: chenkangxian 
  5.  * 
  6.  * @Annotation: 
  7.  * 
  8.  * @Date:2012-4-20 
  9.  *  
  10.  */  
  11. public class test {  
  12.   
  13.     class ConcurrentCount extends Thread{  
  14.           
  15.         ThreadLocal<Integer> count ;  
  16.           
  17.         public ConcurrentCount(ThreadLocal<Integer> count){  
  18.             this.count = count;  
  19.         }  
  20.   
  21.         @Override  
  22.         public void run() {  
  23.               
  24.             for(int i = 0; i < 10; i ++){  
  25.                   
  26.                 if(count.get() != null){  
  27.                     count.set(count.get() + 1);  
  28.                 }else{  
  29.                     count.set(0);  
  30.                 }  
  31.                   
  32.                 System.out.println("Thread: " + this.currentThread().getName() + ", count: " + count.get());  
  33.             }  
  34.               
  35.             return ;  
  36.         }  
  37.           
  38.     }  
  39.       
  40.     /** 
  41.      * Author: chenkangxian 
  42.      * 
  43.      * Last Modification Time: 2012-4-20 
  44.      * 
  45.      * @param args 
  46.      */  
  47.     public static void main(String[] args) {  
  48.   
  49.           
  50.         //所有线程均使用该变量,但是却不存在线程安全问题  
  51.         ThreadLocal<Integer> count = new ThreadLocal<Integer>();  
  52.           
  53.         test test = new test();  
  54.           
  55.         ConcurrentCount count1 = test.new ConcurrentCount(count);  
  56.         ConcurrentCount count2 = test.new ConcurrentCount(count);  
  57.         ConcurrentCount count3 = test.new ConcurrentCount(count);  
  58.         ConcurrentCount count4 = test.new ConcurrentCount(count);  
  59.           
  60.         count1.start();  
  61.         count2.start();  
  62.         count3.start();  
  63.         count4.start();  
  64.   
  65.     }  
  66.   
  67. }  

 

 

ThreadLocal使用归纳有两点:

1.每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。 
2.将一个共用的ThreadLocal实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象。

 

 

ThreadLocal实现的源代码:

 

Java代码  收藏代码
  1. /* 
  2.  * @(#)ThreadLocal.java 1.42 06/06/23 
  3.  * 
  4.  * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 
  5.  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 
  6.  */  
  7.   
  8. package java.lang;  
  9. import java.lang.ref.*;  
  10. import java.util.concurrent.atomic.AtomicInteger;  
  11.   
  12.        new ThreadLocal &lt; Integer > () { 

  13. public class ThreadLocal<T> {  
  14.  
  15.     private final int threadLocalHashCode = nextHashCode();  
  16.   
  17.     /** 
  18.      * The next hash code to be given out. Updated atomically. Starts at 
  19.      * zero. 
  20.      */  
  21.     private static AtomicInteger nextHashCode =   
  22.     new AtomicInteger();  
  23.   
  24.     /** 
  25.      * The difference between successively generated hash codes - turns 
  26.      * implicit sequential thread-local IDs into near-optimally spread 
  27.      * multiplicative hash values for power-of-two-sized tables. 
  28.      */  
  29.     private static final int HASH_INCREMENT = 0x61c88647;  
  30.   
  31.     /** 
  32.      * Returns the next hash code. 
  33.      */  
  34.     private static int nextHashCode() {  
  35.     return nextHashCode.getAndAdd(HASH_INCREMENT);   
  36.     }  
  37.   
  38.   
  39.      * @return the initial value for this thread-local 
  40.      */  
  41.     protected T initialValue() {  
  42.         return null;  
  43.     }  
  44.   
  45.     /** 
  46.      * Creates a thread local variable. 
  47.      */  
  48.     public ThreadLocal() {  
  49.     }  
  50.   
  51.    
  52.     public T get() {  
  53.         Thread t = Thread.currentThread();  
  54.         ThreadLocalMap map = getMap(t);  
  55.         if (map != null) {  
  56.             ThreadLocalMap.Entry e = map.getEntry(this);  
  57.             if (e != null)  
  58.                 return (T)e.value;  
  59.         }  
  60.         return setInitialValue();  
  61.     }  
  62.   

  63.     private T setInitialValue() {  
  64.         T value = initialValue();  
  65.         Thread t = Thread.currentThread();  
  66.         ThreadLocalMap map = getMap(t);  
  67.         if (map != null)  
  68.             map.set(this, value);  
  69.         else  
  70.             createMap(t, value);  
  71.         return value;  
  72.     }  

  73.     public void set(T value) {  
  74.         Thread t = Thread.currentThread();  
  75.         ThreadLocalMap map = getMap(t);  
  76.         if (map != null)  
  77.             map.set(this, value);  
  78.         else  
  79.             createMap(t, value);  
  80.     }  
  81.   

  82.      public void remove() {  
  83.          ThreadLocalMap m = getMap(Thread.currentThread());  
  84.          if (m != null)  
  85.              m.remove(this);  
  86.      }  
  87.   
  88.     /** 
  89.      * Get the map associated with a ThreadLocal. Overridden in 
  90.      * InheritableThreadLocal. 
  91.      * 
  92.      * @param  t the current thread 
  93.      * @return the map 
  94.      */  
  95.     ThreadLocalMap getMap(Thread t) {  
  96.         return t.threadLocals;  
  97.     }  
  98.   
  99.  
  100.     void createMap(Thread t, T firstValue) {  
  101.         t.threadLocals = new ThreadLocalMap(this, firstValue);  
  102.     }  
  103.   

  104.     static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {  
  105.         return new ThreadLocalMap(parentMap);  
  106.     }  
  107.   
  108.   
  109.     T childValue(T parentValue) {  
  110.         throw new UnsupportedOperationException();  
  111.     }  
  112.   
  113.  
  114.     static class ThreadLocalMap {  
  115.   
  116.      
  117.         static class Entry extends WeakReference<ThreadLocal> {  
  118.             /** The value associated with this ThreadLocal. */  
  119.             Object value;  
  120.   
  121.             Entry(ThreadLocal k, Object v) {  
  122.                 super(k);  
  123.                 value = v;  
  124.             }  
  125.         }  
  126.   
  127.         /** 
  128.          * The initial capacity -- MUST be a power of two. 
  129.          */  
  130.         private static final int INITIAL_CAPACITY = 16;  
  131.   
  132.         /** 
  133.          * The table, resized as necessary. 
  134.          * table.length MUST always be a power of two. 
  135.          */  
  136.         private Entry[] table;  
  137.   
  138.         /** 
  139.          * The number of entries in the table. 
  140.          */  
  141.         private int size = 0;  
  142.   
  143.         /** 
  144.          * The next size value at which to resize. 
  145.          */  
  146.         private int threshold; // Default to 0  
  147.   
  148.         /** 
  149.          * Set the resize threshold to maintain at worst a 2/3 load factor. 
  150.          */  
  151.         private void setThreshold(int len) {  
  152.             threshold = len * 2 / 3;  
  153.         }  
  154.   
  155.         /** 
  156.          * Increment i modulo len. 
  157.          */  
  158.         private static int nextIndex(int i, int len) {  
  159.             return ((i + 1 < len) ? i + 1 : 0);  
  160.         }  
  161.   
  162.         /** 
  163.          * Decrement i modulo len. 
  164.          */  
  165.         private static int prevIndex(int i, int len) {  
  166.             return ((i - 1 >= 0) ? i - 1 : len - 1);  
  167.         }  
  168.   
  169.         /** 
  170.          * Construct a new map initially containing (firstKey, firstValue). 
  171.          * ThreadLocalMaps are constructed lazily, so we only create 
  172.          * one when we have at least one entry to put in it. 
  173.          */  
  174.         ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {  
  175.             table = new Entry[INITIAL_CAPACITY];  
  176.             int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);  
  177.             table[i] = new Entry(firstKey, firstValue);  
  178.             size = 1;  
  179.             setThreshold(INITIAL_CAPACITY);  
  180.         }  
  181.   
  182.         /** 
  183.          * Construct a new map including all Inheritable ThreadLocals 
  184.          * from given parent map. Called only by createInheritedMap. 
  185.          * 
  186.          * @param parentMap the map associated with parent thread. 
  187.          */  
  188.         private ThreadLocalMap(ThreadLocalMap parentMap) {  
  189.             Entry[] parentTable = parentMap.table;  
  190.             int len = parentTable.length;  
  191.             setThreshold(len);  
  192.             table = new Entry[len];  
  193.   
  194.             for (int j = 0; j < len; j++) {  
  195.                 Entry e = parentTable[j];  
  196.                 if (e != null) {  
  197.                     ThreadLocal key = e.get();  
  198.                     if (key != null) {  
  199.                         Object value = key.childValue(e.value);  
  200.                         Entry c = new Entry(key, value);  
  201.                         int h = key.threadLocalHashCode & (len - 1);  
  202.                         while (table[h] != null)  
  203.                             h = nextIndex(h, len);  
  204.                         table[h] = c;  
  205.                         size++;  
  206.                     }  
  207.                 }  
  208.             }  
  209.         }  
  210.   
  211.     
  212.         private Entry getEntry(ThreadLocal key) {  
  213.             int i = key.threadLocalHashCode & (table.length - 1);  
  214.             Entry e = table[i];  
  215.             if (e != null && e.get() == key)  
  216.                 return e;  
  217.             else  
  218.                 return getEntryAfterMiss(key, i, e);  
  219.         }  
  220.   
  221.     
  222.         private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {  
  223.             Entry[] tab = table;  
  224.             int len = tab.length;  
  225.   
  226.             while (e != null) {  
  227.                 ThreadLocal k = e.get();  
  228.                 if (k == key)  
  229.                     return e;  
  230.                 if (k == null)  
  231.                     expungeStaleEntry(i);  
  232.                 else  
  233.                     i = nextIndex(i, len);  
  234.                 e = tab[i];  
  235.             }  
  236.             return null;  
  237.         }  
  238.   
  239.         /** 
  240.          * Set the value associated with key. 
  241.          * 
  242.          * @param key the thread local object 
  243.          * @param value the value to be set 
  244.          */  
  245.         private void set(ThreadLocal key, Object value) {  
  246.   
  247.             // We don't use a fast path as with get() because it is at  
  248.             // least as common to use set() to create new entries as  
  249.             // it is to replace existing ones, in which case, a fast  
  250.             // path would fail more often than not.  
  251.   
  252.             Entry[] tab = table;  
  253.             int len = tab.length;  
  254.             int i = key.threadLocalHashCode & (len-1);  
  255.   
  256.             for (Entry e = tab[i];  
  257.          e != null;  
  258.          e = tab[i = nextIndex(i, len)]) {  
  259.                 ThreadLocal k = e.get();  
  260.   
  261.                 if (k == key) {  
  262.                     e.value = value;  
  263.                     return;  
  264.                 }  
  265.   
  266.                 if (k == null) {  
  267.                     replaceStaleEntry(key, value, i);  
  268.                     return;  
  269.                 }  
  270.             }  
  271.   
  272.             tab[i] = new Entry(key, value);  
  273.             int sz = ++size;  
  274.             if (!cleanSomeSlots(i, sz) && sz >= threshold)  
  275.                 rehash();  
  276.         }  
  277.   
  278.         /** 
  279.          * Remove the entry for key. 
  280.          */  
  281.         private void remove(ThreadLocal key) {  
  282.             Entry[] tab = table;  
  283.             int len = tab.length;  
  284.             int i = key.threadLocalHashCode & (len-1);  
  285.             for (Entry e = tab[i];  
  286.          e != null;  
  287.          e = tab[i = nextIndex(i, len)]) {  
  288.                 if (e.get() == key) {  
  289.                     e.clear();  
  290.                     expungeStaleEntry(i);  
  291.                     return;  
  292.                 }  
  293.             }  
  294.         }  
  295.   
  296.    
  297.         private void replaceStaleEntry(ThreadLocal key, Object value,  
  298.                                        int staleSlot) {  
  299.             Entry[] tab = table;  
  300.             int len = tab.length;  
  301.             Entry e;  
  302.   
  303.        
  304.             int slotToExpunge = staleSlot;  
  305.             for (int i = prevIndex(staleSlot, len);  
  306.          (e = tab[i]) != null;  
  307.                  i = prevIndex(i, len))  
  308.                 if (e.get() == null)  
  309.                     slotToExpunge = i;  
  310.   
  311.             // Find either the key or trailing null slot of run, whichever  
  312.             // occurs first  
  313.             for (int i = nextIndex(staleSlot, len);  
  314.          (e = tab[i]) != null;  
  315.                  i = nextIndex(i, len)) {  
  316.                 ThreadLocal k = e.get();  
  317.   
  318.             
  319.                 if (k == key) {  
  320.                     e.value = value;  
  321.   
  322.                     tab[i] = tab[staleSlot];  
  323.                     tab[staleSlot] = e;  
  324.   
  325.                     // Start expunge at preceding stale entry if it exists  
  326.                     if (slotToExpunge == staleSlot)  
  327.                         slotToExpunge = i;  
  328.                     cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);  
  329.                     return;  
  330.                 }  
  331.   
  332.       
  333.                 if (k == null && slotToExpunge == staleSlot)  
  334.                     slotToExpunge = i;  
  335.             }  
  336.   
  337.             // If key not found, put new entry in stale slot  
  338.             tab[staleSlot].value = null;     
  339.             tab[staleSlot] = new Entry(key, value);  
  340.   
  341.             // If there are any other stale entries in run, expunge them  
  342.             if (slotToExpunge != staleSlot)  
  343.                 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);  
  344.         }  
  345.   
  346.     
  347.         private int expungeStaleEntry(int staleSlot) {  
  348.             Entry[] tab = table;  
  349.             int len = tab.length;  
  350.   
  351.             // expunge entry at staleSlot  
  352.             tab[staleSlot].value = null;     
  353.             tab[staleSlot] = null;  
  354.             size--;  
  355.   
  356.             // Rehash until we encounter null  
  357.             Entry e;  
  358.             int i;  
  359.             for (i = nextIndex(staleSlot, len);  
  360.          (e = tab[i]) != null;  
  361.                  i = nextIndex(i, len)) {  
  362.                 ThreadLocal k = e.get();  
  363.                 if (k == null) {  
  364.                     e.value = null;  
  365.                     tab[i] = null;  
  366.                     size--;  
  367.                 } else {  
  368.                     int h = k.threadLocalHashCode & (len - 1);  
  369.                     if (h != i) {  
  370.                         tab[i] = null;  
  371.   
  372.                         // Unlike Knuth 6.4 Algorithm R, we must scan until  
  373.                         // null because multiple entries could have been stale.  
  374.                         while (tab[h] != null)  
  375.                             h = nextIndex(h, len);  
  376.                         tab[h] = e;  
  377.                     }  
  378.                 }  
  379.             }  
  380.             return i;  
  381.         }  
  382.   
  383.  
  384.         private boolean cleanSomeSlots(int i, int n) {  
  385.             boolean removed = false;  
  386.             Entry[] tab = table;  
  387.             int len = tab.length;  
  388.             do {  
  389.                 i = nextIndex(i, len);  
  390.                 Entry e = tab[i];  
  391.                 if (e != null && e.get() == null) {  
  392.                     n = len;  
  393.                     removed = true;  
  394.                     i = expungeStaleEntry(i);  
  395.                 }  
  396.             } while ( (n >>>= 1) != 0);  
  397.             return removed;  
  398.         }  
  399.   
  400.     
  401.         private void rehash() {  
  402.             expungeStaleEntries();  
  403.   
  404.             // Use lower threshold for doubling to avoid hysteresis  
  405.             if (size >= threshold - threshold / 4)  
  406.                 resize();  
  407.         }  
  408.   
  409.         /** 
  410.          * Double the capacity of the table. 
  411.          */  
  412.         private void resize() {  
  413.             Entry[] oldTab = table;  
  414.             int oldLen = oldTab.length;  
  415.             int newLen = oldLen * 2;  
  416.             Entry[] newTab = new Entry[newLen];  
  417.             int count = 0;  
  418.   
  419.             for (int j = 0; j < oldLen; ++j) {  
  420.                 Entry e = oldTab[j];  
  421.                 if (e != null) {  
  422.                     ThreadLocal k = e.get();  
  423.                     if (k == null) {  
  424.                         e.value = null// Help the GC  
  425.                     } else {  
  426.                         int h = k.threadLocalHashCode & (newLen - 1);  
  427.                         while (newTab[h] != null)  
  428.                             h = nextIndex(h, newLen);  
  429.                         newTab[h] = e;  
  430.                         count++;  
  431.                     }  
  432.                 }  
  433.             }  
  434.   
  435.             setThreshold(newLen);  
  436.             size = count;  
  437.             table = newTab;  
  438.         }  
  439.   
  440.         /** 
  441.          * Expunge all stale entries in the table. 
  442.          */  
  443.         private void expungeStaleEntries() {  
  444.             Entry[] tab = table;  
  445.             int len = tab.length;  
  446.             for (int j = 0; j < len; j++) {  
  447.                 Entry e = tab[j];  
  448.                 if (e != null && e.get() == null)  
  449.                     expungeStaleEntry(j);  
  450.             }  
  451.         }  
  452.     }  
  453. }  

<script type="text/javascript"><!--google_ad_client = "ca-pub-1944176156128447";/* cnblogs 首页横幅 */google_ad_slot = "5419468456";google_ad_width = 728;google_ad_height = 90;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 公司注册了商标怎么办 2018年属兔不适合结婚怎么办 交通事故对方没钱赔怎么办 滴滴快车出车祸怎么办 交警法院扣车怎么办 肇事逃逸没钱赔怎么办 撞死人无力赔偿怎么办 宝贝咳嗽很厉害怎么办 开车有人别车怎么办 判刑罚金交不起怎么办 罚金太多交不起怎么办 判决书上的罚金怎么办 刑事罚款不交怎么办 缎面的鞋脏了怎么办 夏天脸上长粉刺怎么办 鬃狮不吃东西怎么办 买二手车的车牌怎么办 榴莲打开了肉没熟怎么办 gmat的prep做完怎么办 电脑没ip地址怎么办 电脑ip地址缺失怎么办 cos还原不了人物怎么办 执行局抓人十五天不放人怎么办 笔记本电脑键盘按键错乱怎么办 靠墙倒立上不去怎么办 医院多收钱了怎么办 学生总是转笔怎么办 吃错东西呕吐怎么办 手腕筋扭伤了怎么办 右膝盖内侧疼痛怎么办 膝盖关节腔积液怎么办 小孩玩游戏花钱怎么办 初三玩手机上瘾怎么办 无线网有感叹号怎么办 台式电脑网络感叹号怎么办 忘记发红包密码怎么办 电信诈骗被骗后怎么办 C小孩讨厌上学怎么办 军训来大姨妈怎么办 夏天军训来月经怎么办 军训遇到大姨妈怎么办