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的使用及源码分析

并发编程学习笔记(8)----ThreadLocal的使用及源码分析的更多相关文章

  1. 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理

    1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...

  2. 并发编程学习笔记(6)----公平锁和ReentrantReadWriteLock使用及原理

    (一)公平锁 1.什么是公平锁? 公平锁指的是在某个线程释放锁之后,等待的线程获取锁的策略是以请求获取锁的时间为标准的,即使先请求获取锁的线程先拿到锁. 2.在java中的实现? 在java的并发包中 ...

  3. Java并发编程学习笔记

    Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...

  4. 并发编程学习笔记(15)----Executor框架的使用

    Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...

  5. 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理

    1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...

  6. 并发编程学习笔记(13)----ConcurrentLinkedQueue(非阻塞队列)和BlockingQueue(阻塞队列)原理

    · 在并发编程中,我们有时候会需要使用到线程安全的队列,而在Java中如果我们需要实现队列可以有两种方式,一种是阻塞式队列.另一种是非阻塞式的队列,阻塞式队列采用锁来实现,而非阻塞式队列则是采用cas ...

  7. 并发编程学习笔记(11)----FutureTask的使用及实现

    1. Future的使用 Future模式解决的问题是.在实际的运用场景中,可能某一个任务执行起来非常耗时,如果我们线程一直等着该任务执行完成再去执行其他的代码,就会损耗很大的性能,而Future接口 ...

  8. 并发编程学习笔记(12)----Fork/Join框架

    1. Fork/Join 的概念 Fork指的是将系统进程分成多个执行分支(线程),Join即是等待,当fork()方法创建了多个线程之后,需要等待这些分支执行完毕之后,才能得到最终的结果,因此joi ...

  9. 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理

    在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...

随机推荐

  1. ibatis 入门

     iBatis 简单介绍: iBatis 是apache 的一个开源项目.一个O/R Mapping 解决方式,iBatis 最大的特点就是小巧.上手非常快.假设不须要太多复杂的功能.iBatis ...

  2. php把时间计算成几分钟前,几小时前,几天前的函数

    function time_tran($the_time){ $now_time = date("Y-m-d H:i:s",time()+8*60*60); $now_time = ...

  3. [Python网络编程]浅析守护进程后台任务的设计与实现

    在做基于B/S应用中.常常有须要后台执行任务的需求,最简单比方发送邮件.在一些如防火墙,WAF等项目中,前台仅仅是为了展示内容与各种參数配置.后台守护进程才是重头戏.所以在防火墙配置页面中可能会常常看 ...

  4. web 开发之js---js获取select标签选中的值

    var obj = document.getElementByIdx_x(”testSelect”); //定位id var index = obj.selectedIndex; // 选中索引 va ...

  5. JavaScript基础 -- 常见DOM树操作

    1.创建并增加元素节点 <ul id="ul"> <li>1</li> <li>2</li> <li>3&l ...

  6. 报表应用系列——图表JFreeChart: 第 4 章 折线图

    双击代码全选 1 2 3 4 5 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(100 ...

  7. 【转】Material Design 折叠效果 Toolbar CollapsingToolbarLayout AppBarLayout

    我非常喜欢Material Design里折叠工具栏的效果,bilibili Android客户端视频详情页就是采用的这种设计.这篇文章的第二部分我们就通过简单的模仿bilibili视频详情页的实现来 ...

  8. idea mac 控制台中文乱码

    参考:https://blog.csdn.net/lheangus/article/details/48915357 修改内容 -Dfile.encoding=UTF-8

  9. Python: PS 滤镜--万花筒效果

    本文用 Python 实现 PS 的一种滤镜效果,称为万花筒.也是对图像做各种扭曲变换,最后图像呈现的效果就像从万花筒中看到的一样: 图像的效果可以参考之前的博客: http://blog.csdn. ...

  10. G. 铁路修复计划 最小生成树

    G. 铁路修复计划 二分答案,改变边的权值,找最小生成树即可. 类似的思想还可以用在单度限制最小生成树和最优比例生成树上. #include<iostream> #include<c ...