1.简述ThreadLocal

  ThreadLocal实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。ThreadLocal是一个线程级别的局部变量,下面是线程局部变量(ThreadLocal variables)的关键点:

  A、当使用ThreadLocal维护变量时,若多个线程访问ThreadLocal实例,ThreadLocal为每个使用该变量的线程提供了一个独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。

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

2.细看ThreadLocal

ThreadLocal<T>类很简单,只有四个方法:

(1)void set(T value),该方法用来设置当前线程中变量的副本

(2)public T get(),该方法是用来获取ThreadLocal在当前线程中保存的变量副本

(3)public void remove(),该方法用来移除当前线程中变量的副本,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束以后,对应线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

(4)protected T initialValue(),该方法是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法,ThreadLocal中的缺省实现直接返回一个null。

3.ThreadLocal示例

简单的使用方法如下:

  1. package com.test;
  2.  
  3. public class ThreadMain {
  4. // ①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
  5. private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
  6. public Integer initialValue() {
  7. return 0;
  8. }
  9. };
  10.  
  11. // ②获取下一个序列值
  12. public int getNextNum() {
  13. seqNum.set(seqNum.get() + 1);
  14. return seqNum.get();
  15. }
  16.  
  17. public static void main(String[] args) {
  18. ThreadMain sn = new ThreadMain();
  19. // ③ 3个线程共享sn,各自产生序列号
  20. TestClient t1 = new TestClient(sn);
  21. TestClient t2 = new TestClient(sn);
  22. TestClient t3 = new TestClient(sn);
  23. t1.start();
  24. t2.start();
  25. t3.start();
  26. }
  27.  
  28. private static class TestClient extends Thread {
  29. private ThreadMain sn;
  30.  
  31. public TestClient(ThreadMain sn) {
  32. this.sn = sn;
  33. }
  34.  
  35. public void run() {
  36. for (int i = 0; i < 3; i++) {
  37. // ④每个线程打出3个序列值
  38. System.out.println("thread[" + Thread.currentThread().getName()
  39. + "] --> sn[" + sn.getNextNum() + "]");
  40. }
  41. }
  42. }
  43.  
  44. }

结果如下:

  1. thread[Thread-0] --> sn[1]
  2. thread[Thread-2] --> sn[1]
  3. thread[Thread-1] --> sn[1]
  4. thread[Thread-2] --> sn[2]
  5. thread[Thread-0] --> sn[2]
  6. thread[Thread-2] --> sn[3]
  7. thread[Thread-1] --> sn[2]
  8. thread[Thread-1] --> sn[3]
  9. thread[Thread-0] --> sn[3]

另一个案例

  1. package com.csu.thread;
  2.  
  3. class GlobalVarManager {
  4. private static ThreadLocal<String> globalVars = new ThreadLocal<String>(){
  5. protected String initialValue() {
  6. return "hello";
  7. }
  8. };
  9.  
  10. public static ThreadLocal<String> getglobalVars() {
  11. return globalVars;
  12. }
  13. }
  14.  
  15. class ThreadRun implements Runnable {
  16.  
  17. private ThreadLocal<String> t;
  18. private String str;
  19.  
  20. ThreadRun(ThreadLocal<String> temp, String s) {
  21. this.t = temp;
  22. this.str = s;
  23. }
  24.  
  25. @Override
  26. public void run() {
  27. System.out.println(Thread.currentThread()+"改变前:" + t.get());
  28. t.set(str);
  29. System.out.println(Thread.currentThread()+"改变后:" + t.get());
  30. }
  31. }
  32.  
  33. public class ThreadLocalTry {
  34.  
  35. public static void main(String[] args) {
  36. for (int i =1; i < 5; i++) {
  37. new Thread(new ThreadRun(GlobalVarManager.getglobalVars(), ""+i)).start();
  38. }
  39. }
  40. }

结果如下:

  1. Thread[Thread-0,5,main]改变前:hello
  2. Thread[Thread-1,5,main]改变前:hello
  3. Thread[Thread-0,5,main]改变后:1
  4. Thread[Thread-2,5,main]改变前:hello
  5. Thread[Thread-1,5,main]改变后:2
  6. Thread[Thread-2,5,main]改变后:3
  7. Thread[Thread-3,5,main]改变前:hello
  8. Thread[Thread-3,5,main]改变后:4

上述案例也可按如下方式来实现:

  1. package com.csu.test;
  2.  
  3. class GlobalVarManager {
  4. private static ThreadLocal<String> globalVars = new ThreadLocal<String>(){
  5. protected String initialValue() {
  6. return "hello";
  7. }
  8. };
  9.  
  10. public static String getGlobalVars() {
  11. return globalVars.get();
  12. }
  13.  
  14. public static void setGlobalVars(String str) {
  15. globalVars.set(str);
  16. }
  17. }
  18.  
  19. class ThreadRun implements Runnable {
  20.  
  21. private String str = null;
  22.  
  23. public ThreadRun(String temp) {
  24. str = temp;
  25. }
  26.  
  27. @Override
  28. public void run() {
  29. System.out.println(Thread.currentThread()+"改变前:" + GlobalVarManager.getGlobalVars());
  30. GlobalVarManager.setGlobalVars(str);
  31. System.out.println(Thread.currentThread()+"改变后:" + GlobalVarManager.getGlobalVars());
  32. }
  33. }
  34.  
  35. public class ThreadLocalTest {
  36.  
  37. public static void main(String[] args) {
  38. for (int i = 1; i < 5; i++) {
  39. new Thread(new ThreadRun("" + i)).start();
  40. }
  41. }
  42. }

结果如下:

  1. Thread[Thread-3,5,main]改变前:hello
  2. Thread[Thread-2,5,main]改变前:hello
  3. Thread[Thread-1,5,main]改变前:hello
  4. Thread[Thread-0,5,main]改变前:hello
  5. Thread[Thread-1,5,main]改变后:2
  6. Thread[Thread-2,5,main]改变后:3
  7. Thread[Thread-3,5,main]改变后:4
  8. Thread[Thread-0,5,main]改变后:1

4.ThreadLocal的实现机制

此部分内容暂没有深入研究,欲了解更多内容请参考https://www.cnblogs.com/dennyzhangdd/p/7978455.html

(1)get()方法源码如下:

(2)set()方法源码如下:

(3)remove()方法源码如下:

(4)上述几个函数涉及到如下两个函数

从前述源码可以看出,ThreadLocal的get、set、remove方法都是操作当前线程,而从Thread的源码可以看出该类有一个ThreadLocal.ThreadLocalMap类型的变量threadLocals,该变量在初次调用ThreadLocal的set()方法时通过createMap()方法初始化

5.ThreadLocalMap

ThreadLocalMap的部分源码如下:

  1. /**
  2. * ThreadLocalMap is a customized hash map suitable only for
  3. * maintaining thread local values. No operations are exported
  4. * outside of the ThreadLocal class. The class is package private to
  5. * allow declaration of fields in class Thread. To help deal with
  6. * very large and long-lived usages, the hash table entries use
  7. * WeakReferences for keys. However, since reference queues are not
  8. * used, stale entries are guaranteed to be removed only when
  9. * the table starts running out of space.
  10. */
  11. static class ThreadLocalMap {
  12.  
  13. /**
  14. * The entries in this hash map extend WeakReference, using
  15. * its main ref field as the key (which is always a
  16. * ThreadLocal object). Note that null keys (i.e. entry.get()
  17. * == null) mean that the key is no longer referenced, so the
  18. * entry can be expunged from table. Such entries are referred to
  19. * as "stale entries" in the code that follows.
  20. */
  21. static class Entry extends WeakReference<ThreadLocal<?>> {
  22. /** The value associated with this ThreadLocal. */
  23. Object value;
  24.  
  25. Entry(ThreadLocal<?> k, Object v) {
  26. super(k);
  27. value = v;
  28. }
  29. }
  30.  
  31. /**
  32. * The initial capacity -- MUST be a power of two.
  33. */
  34. private static final int INITIAL_CAPACITY = 16;
  35.  
  36. /**
  37. * The table, resized as necessary.
  38. * table.length MUST always be a power of two.
  39. */
  40. private Entry[] table;
  41.  
  42. /**
  43. * The number of entries in the table.
  44. */
  45. private int size = 0;
  46.  
  47. /**
  48. * The next size value at which to resize.
  49. */
  50. private int threshold; // Default to 0
  51.  
  52. /**
  53. * Set the resize threshold to maintain at worst a 2/3 load factor.
  54. */
  55. private void setThreshold(int len) {
  56. threshold = len * 2 / 3;
  57. }
  58.  
  59. /**
  60. * Increment i modulo len.
  61. */
  62. private static int nextIndex(int i, int len) {
  63. return ((i + 1 < len) ? i + 1 : 0);
  64. }
  65.  
  66. /**
  67. * Decrement i modulo len.
  68. */
  69. private static int prevIndex(int i, int len) {
  70. return ((i - 1 >= 0) ? i - 1 : len - 1);
  71. }
  72.  
  73. /**
  74. * Construct a new map initially containing (firstKey, firstValue).
  75. * ThreadLocalMaps are constructed lazily, so we only create
  76. * one when we have at least one entry to put in it.
  77. */
  78. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  79. table = new Entry[INITIAL_CAPACITY];
  80. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  81. table[i] = new Entry(firstKey, firstValue);
  82. size = 1;
  83. setThreshold(INITIAL_CAPACITY);
  84. }
  85.  
  86. /**
  87. * Construct a new map including all Inheritable ThreadLocals
  88. * from given parent map. Called only by createInheritedMap.
  89. *
  90. * @param parentMap the map associated with parent thread.
  91. */
  92. private ThreadLocalMap(ThreadLocalMap parentMap) {
  93. Entry[] parentTable = parentMap.table;
  94. int len = parentTable.length;
  95. setThreshold(len);
  96. table = new Entry[len];
  97.  
  98. for (int j = 0; j < len; j++) {
  99. Entry e = parentTable[j];
  100. if (e != null) {
  101. @SuppressWarnings("unchecked")
  102. ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
  103. if (key != null) {
  104. Object value = key.childValue(e.value);
  105. Entry c = new Entry(key, value);
  106. int h = key.threadLocalHashCode & (len - 1);
  107. while (table[h] != null)
  108. h = nextIndex(h, len);
  109. table[h] = c;
  110. size++;
  111. }
  112. }
  113. }
  114. }

此处重点关注一下ThreadLocalMap中的几个成员变量及方法

(1)private Entry[] table;

table是一个Entry类型的数组,该变量在ThreadLocalMap的构造函数中初始化

Entry是ThreadLocalMap的一个内部类

(2)set()方法

  1. /**
  2. * Set the value associated with key.
  3. *
  4. * @param key the thread local object
  5. * @param value the value to be set
  6. */
  7. private void set(ThreadLocal<?> key, Object value) {
  8.  
  9. // We don't use a fast path as with get() because it is at
  10. // least as common to use set() to create new entries as
  11. // it is to replace existing ones, in which case, a fast
  12. // path would fail more often than not.
  13.  
  14. Entry[] tab = table;
  15. int len = tab.length;
  16. int i = key.threadLocalHashCode & (len-1);
  17.  
  18. for (Entry e = tab[i];
  19. e != null;
  20. e = tab[i = nextIndex(i, len)]) {
  21. ThreadLocal<?> k = e.get();
  22.  
  23. if (k == key) {
  24. e.value = value;
  25. return;
  26. }
  27.  
  28. if (k == null) {
  29. replaceStaleEntry(key, value, i);
  30. return;
  31. }
  32. }
  33.  
  34. tab[i] = new Entry(key, value);
  35. int sz = ++size;
  36. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  37. rehash();
  38. }

(3)getEntry()方法

  1. /**
  2. * Get the entry associated with key. This method
  3. * itself handles only the fast path: a direct hit of existing
  4. * key. It otherwise relays to getEntryAfterMiss. This is
  5. * designed to maximize performance for direct hits, in part
  6. * by making this method readily inlinable.
  7. *
  8. * @param key the thread local object
  9. * @return the entry associated with key, or null if no such
  10. */
  11. private Entry getEntry(ThreadLocal<?> key) {
  12. int i = key.threadLocalHashCode & (table.length - 1);
  13. Entry e = table[i];
  14. if (e != null && e.get() == key)
  15. return e;
  16. else
  17. return getEntryAfterMiss(key, i, e);
  18. }
  19.  
  20. /**
  21. * Version of getEntry method for use when key is not found in
  22. * its direct hash slot.
  23. *
  24. * @param key the thread local object
  25. * @param i the table index for key's hash code
  26. * @param e the entry at table[i]
  27. * @return the entry associated with key, or null if no such
  28. */
  29. private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
  30. Entry[] tab = table;
  31. int len = tab.length;
  32.  
  33. while (e != null) {
  34. ThreadLocal<?> k = e.get();
  35. if (k == key)
  36. return e;
  37. if (k == null)
  38. expungeStaleEntry(i);
  39. else
  40. i = nextIndex(i, len);
  41. e = tab[i];
  42. }
  43. return null;
  44. }

(4)remove()方法

  1. /**
  2. * Remove the entry for key.
  3. */
  4. private void remove(ThreadLocal<?> key) {
  5. Entry[] tab = table;
  6. int len = tab.length;
  7. int i = key.threadLocalHashCode & (len-1);
  8. for (Entry e = tab[i];
  9. e != null;
  10. e = tab[i = nextIndex(i, len)]) {
  11. if (e.get() == key) {
  12. e.clear();
  13. expungeStaleEntry(i);
  14. return;
  15. }
  16. }
  17. }

6.总结

ThreadLocal一般都是声明在静态变量中,如果不断地创建ThreadLocal而没有调用其remove方法,将导致内存泄露,特别是在高并发的Web容器当中。

ThreadLocal在处理线程的局部变量时比synchronized同步机制解决线程安全问题更简单,更方便,且程序拥有更高的并发性。

java并发:线程同步机制之ThreadLocal的更多相关文章

  1. Java 并发 线程同步

    Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大 ...

  2. java synchronized 线程同步机制详解

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this ...

  3. Java并发——线程同步Volatile与Synchronized详解

    0. 前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52370068 面试时很可能遇到这样一个问题:使用volatile修饰in ...

  4. 【总结】Java线程同步机制深刻阐述

    原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread ...

  5. ThreadLocal和线程同步机制对比

    共同点: ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题. 区别: 在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量. 这时该变量是多个线程共享的,使用同 ...

  6. Java多线程编程(4)--线程同步机制

    一.锁 1.锁的概念   线程安全问题的产生是因为多个线程并发访问共享数据造成的,如果能将多个线程对共享数据的并发访问改为串行访问,即一个共享数据同一时刻只能被一个线程访问,就可以避免线程安全问题.锁 ...

  7. Java多线程 | 02 | 线程同步机制

    同步机制简介 ​ 线程同步机制是一套用于协调线程之间的数据访问的机制.该机制可以保障线程安全.Java平台提供的线程同步机制包括: 锁,volatile关键字,final关键字,static关键字,以 ...

  8. Java分享笔记:创建多线程 & 线程同步机制

    [1] 创建多线程的两种方式 1.1 通过继承Thread类创建多线程 1.定义Thread类的子类,重写run()方法,在run()方法体中编写子线程要执行的功能. 2.创建子线程的实例对象,相当于 ...

  9. (转)Java并发编程:深入剖析ThreadLocal

    Java并发编程:深入剖析ThreadLoca Java并发编程:深入剖析ThreadLocal 说下自己的理解:使用ThreadLocal能够实现空间换时间,重在理解ThreadLocal是如何复制 ...

随机推荐

  1. Sqlite学习笔记(二)&&性能测试

    测试目标 获取SQlite的常规性能指标 测试环境 CPU:8核,Intel(R) Xeon(R) CPU E5-2430 0 @ 2.20GHz 内存:16G 磁盘:SSD Linux 2.6.32 ...

  2. InnoDB源码分析--缓冲池(二)

    转载请附原文链接:http://www.cnblogs.com/wingsless/p/5578727.html 上一篇中我简单的分析了一下InnoDB缓冲池LRU算法的相关源码,其实说不上是分析,应 ...

  3. MongoDB3.0.x版本用户授权配置(单机环境)

    MongoDB数据库默认情况下是没有做权限控制的,只要能够连接所开放的端口就能进行访问,而且拥有root级别的权限:对于生产环境而言是极不安全的,所以需要建立用户,进行授权控制. 单机环境下的用户授权 ...

  4. Python 标准异常

      异常名称 描述 BaseException 所有异常的基类 SystemExit 解释器请求退出 KeyboardInterrupt 用户中断执行(通常是输入^C) Exception 常规错误的 ...

  5. KVM 介绍(6):Nova 通过 libvirt 管理 QEMU/KVM 虚机 [Nova Libvirt QEMU/KVM Domain]

    学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...

  6. margin-top无效的解决方法

    先上代码: <div id="content" style=" width:750px; height:300px; background:#C29A29; mar ...

  7. monkey学习笔记

    Monkey一. Monkey 是什么?Monkey是Android中的一个命令行工具,它其实就是SDK中附带的一个工具,可以运行在模拟器里或实际设备中. 二.Monkey 测试的目的?Monkey测 ...

  8. UART to Serial Terminal(转载)

    前一篇<UART Explained>介绍了UART的基本信息,重点分析了UART的信号.本文摘录的文章则重点介绍了波特率(Baud Rate)相关的内容,波特率越高,传输速度越快,但实际 ...

  9. Codeforces 460D Little Victor and Set --分类讨论+构造

    题意:从区间[L,R]中选取不多于k个数,使这些数异或和尽量小,输出最小异或和以及选取的那些数. 解法:分类讨论. 设选取k个数. 1. k=4的时候如果区间长度>=4且L是偶数,那么可以构造四 ...

  10. CF687C. The Values You Can Make[背包DP]

    C. The Values You Can Make time limit per test 2 seconds memory limit per test 256 megabytes input s ...