1. ThreadLocal的理解

  ThreadLocal,顾名思义,就是线程的本地变量,ThreadLocal会为每个线程创建一个本地变量副本,使得使用ThreadLocal管理的变量在多线程的环境下,每个线程都是访问的是自己内部的副本变量,将全局变量局部化,跟局部变量一样的使用,从而避免了一些线程安全的问题。比如在数据库连接池中获取的连接时,此时的连接对于每个线程来说,都应该是独立的,所以可以使用ThreadLocal来管理该线程的连接。从而避免在多线程环境下的连接的安全问题。

2. ThreadLocal的使用方式

  ThreaLocal的使用方式也是非常简单的,直接创建一个全局的ThreadLocal,并初始化,这里需要注意的是直接new ThreadLocal时这里ThreadLocal管理的对象是null的,所以如果需要在创建对象的时候就初始化对象的话,可以重写ThreadLocal中的InitialValue方法来进行初始化,初始化完成之后,就是使用了,调用ThreadLocal的get方法拿到被管理的对象,操作对象,再将修改后的对象通过ThreadLocal的set方法保存到ThreadLocal中。代码如下:

public class ThreadLoclDemo {

    private ThreadLocal<Integer> nextc = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return Integer.valueOf(0);
}
}; public int getNext () {
Integer integer = nextc.get();
integer++;
nextc.set(integer);
return integer;
} public static void main(String[] args) {
final ThreadLoclDemo threadLoclDemo = new ThreadLoclDemo();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " " + threadLoclDemo.getNext());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " " + threadLoclDemo.getNext());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " " + threadLoclDemo.getNext());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " " + threadLoclDemo.getNext());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}

  这里通过五个线程去执行getNext()方法,输出如下:

Thread-0 1
Thread-1 1
Thread-2 1
Thread-3 1
Thread-1 2
Thread-2 2
Thread-0 2
Thread-3 2
Thread-1 3
Thread-0 3
Thread-2 3
Thread-2 4
Thread-1 4
Thread-0 4
Thread-3 3
Thread-0 5
Thread-2 5
Thread-1 5
Thread-2 6
Thread-1 6
Thread-0 6
Thread-3 4
Thread-2 7
Thread-0 7
Thread-1 7
Thread-1 8

  可以看出,对于每个线程,它们锁获取到的值都是自增1之后的值,并没有出现线程安全问题或是线程之间由于操作共享数据而相互影响的情况,输出犹如线程在使用局部变量一样清晰,这就是ThreadLocal的使用方式和作用。

 3.ThreadLocal的源码分析

  ThreadLocal的源码还是比较简单的,它的代码之中我们只需要关注get()/set()/remove()三个方法即可

  首先看set()方法:

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

  首先获取当前线程,通过getMap()传入当前线程去获取ThreadLocalMap,ThreadLocalMap属于Thread的一个内部属性threadLocals,所以通过当前线程就可以获取当前线程所对应的ThreadLocalMap,如果当前map不为空,表示当前线程已经被创建,则以当前线程为key,修改value为值,保存变量,如果为空,则通过createMap()新建一个ThreadLocalMap对象,并将其赋值给当前Thread对象的threadLocals属性。这就完成了set()的操作。

  get()方法:

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}

  获取当前线程,通过当前线程获取ThreadLocalMap,如果map为空,则返回setInitialValue()方法,这里此方法返回的是null,所以在最开始初始化时,被管理的对象的值为null也是这个原因,可以重写initialValue()方法来进行初始化。如果map不为空,则通过map中的getEntry()方法获取ThreadLocalMap中的entry对象,所以ThreadLocal管理的对象实际上时存储在ThreadLocalMap的Entry内部类中的value中的,如果entry存在,直接返回e.value,也就是ThreadLocal管理的对象,否则返回初始化的值(null)。

  remove()方法:

 public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}

  remove()方法很简单,获取当前线程的ThreadLocalMap,不为空则调用ThreadLocalMap的remove()方法。直接移除当前ThreadLocalMap中的Entry对象,即移除了当前线程和ThreadLocal为当前线程创建的内部副本。

  ThreadLocal的分享就到这里,不足之处,请各位多多指教。

原文 并发编程学习笔记(8)----ThreadLocal的使用及源码分析

最新文章

  1. AutoMapper实现自动CreapMap
  2. Castle ActiveRecord相关错误
  3. React Native 开发之 (06) JSX
  4. goldengate 12c 12.2 新特性(updated)
  5. 【洛谷P1541】乌龟棋
  6. 查看Eclipse中的jar包的源代码:jd-gui.exe
  7. 关于gcd函数解最大公约数
  8. oracle游标循环的嵌套
  9. QT学习(对话框)codeblock版本
  10. Ueditor自定义默认宽度高度
  11. c#中使用ABCpdf处理PDF
  12. Android:Service的非绑定式的创建和生命周期
  13. Cloud Foundry warden container 安全性探讨
  14. ar1220f-s四条拨号光纤做的策略路由实现负载均衡
  15. Zabbix配置邮件监控
  16. Mac OSX安装启动 zookeeper
  17. CSS实例:图片导航块
  18. 前端UI框架总结
  19. 【Tensorflow】设置显存自适应,显存比例
  20. iOS - UITabBarController中的坑

热门文章

  1. Oracle 远程访问配置 在 Windows Forms 和 WPF 应用中使用 FontAwesome 图标 C#反序列化XML异常:在 XML文档(0, 0)中有一个错误“缺少根元素” C#[Win32&amp;WinCE&amp;WM]应用程序只能运行一个实例:MutexHelper Decimal类型截取保留N位小数向上取, Decimal类型截取保留N位小数并且不进行四舍五入操作
  2. Ubuntu18.04系统中vi键盘输入字符不匹配
  3. MySQL测试代码
  4. Oracle利用游标返回结果集的的例子(C#)...(最爱)
  5. T4 最小差异值 dvalue
  6. hdu 5782(kmp+hash)
  7. Objective-C 对象的类型与动态结合
  8. Rails5&#160;layout&#160;和&#160;template
  9. bzoj 1718: [Usaco2006 Jan] Redundant Paths 分离的路径【tarjan】
  10. 10.12NOIP模拟题(2)