并发编程学习笔记(8)----ThreadLocal的使用及源码分析
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的使用及源码分析的更多相关文章
- 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理
1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...
- 并发编程学习笔记(6)----公平锁和ReentrantReadWriteLock使用及原理
(一)公平锁 1.什么是公平锁? 公平锁指的是在某个线程释放锁之后,等待的线程获取锁的策略是以请求获取锁的时间为标准的,即使先请求获取锁的线程先拿到锁. 2.在java中的实现? 在java的并发包中 ...
- Java并发编程学习笔记
Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...
- 并发编程学习笔记(15)----Executor框架的使用
Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...
- 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理
1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...
- 并发编程学习笔记(13)----ConcurrentLinkedQueue(非阻塞队列)和BlockingQueue(阻塞队列)原理
· 在并发编程中,我们有时候会需要使用到线程安全的队列,而在Java中如果我们需要实现队列可以有两种方式,一种是阻塞式队列.另一种是非阻塞式的队列,阻塞式队列采用锁来实现,而非阻塞式队列则是采用cas ...
- 并发编程学习笔记(11)----FutureTask的使用及实现
1. Future的使用 Future模式解决的问题是.在实际的运用场景中,可能某一个任务执行起来非常耗时,如果我们线程一直等着该任务执行完成再去执行其他的代码,就会损耗很大的性能,而Future接口 ...
- 并发编程学习笔记(12)----Fork/Join框架
1. Fork/Join 的概念 Fork指的是将系统进程分成多个执行分支(线程),Join即是等待,当fork()方法创建了多个线程之后,需要等待这些分支执行完毕之后,才能得到最终的结果,因此joi ...
- 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理
在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...
随机推荐
- 华为OJ2051-最小的K个数(Top K问题)
一.题目描述 描述: 输入n个整数,输出其中最小的k个. 输入: 输入 n 和 k 输入一个整数数组 输出: 输出一个整数数组 样例输入: 5 2 1 3 5 7 2 样例输出: 1 2 二.Top ...
- andriod socket开发问题小结
andriod socket开发问题小结 个人信息:就读于燕大本科软件project专业 眼下大四; 本人博客:google搜索"cqs_2012"就可以; 个人爱好:酷爱数据结构 ...
- mysql正则表达式及应用
mysql where子句的模式匹配 今天在应用中遇到了这样的一个问题,有一个字段 t1,其中的值类似于:1,1,1,2,3,3,4,4,5,5,2,4,3,2,1,2 需要从里面搜索出比如说:第一个 ...
- jacoco+maven生成单元测试覆盖率报告
参考:https://www.petrikainulainen.net/programming/maven/creating-code-coverage-reports-for-unit-and-in ...
- WPF中的常用布局 栈的实现 一个关于素数的神奇性质 C# defualt关键字默认值用法 接口通俗理解 C# Json序列化和反序列化 ASP.NET CORE系列【五】webapi整理以及RESTful风格化
WPF中的常用布局 一 写在开头1.1 写在开头微软是一家伟大的公司.评价一门技术的好坏得看具体的需求,没有哪门技术是面面俱到地好,应该抛弃对微软和微软的技术的偏见. 1.2 本文内容本文主要内容 ...
- 浅析分布式数据库中间件DDM
前言 DDM是什么?这是华为云Paas推出的分布式数据库中间件,DDM(Distributed Database Middleware)是一个实现了Mysql协议栈的服务器,前端用户可以把它看做一个数 ...
- Cocos2d-x编译Android环境
1.Android环境搭配: 下载jdk 下载Android ADT 下载安装Android SDK,地址:http://developer.android.com/sdk/index.html#do ...
- 【Android】使用 SwipeRefreshLayout 实现下拉刷新
今天在codepath 上看到一个开源项目 [点击查看]使用到了 SwipeRefreshLayout 实现了下拉刷新,但演示样例并不完整,于是自己就动手写了下.之前看到郭霖的博客上也有介绍下拉刷新, ...
- CI框架下的get_instance() 函数
你随便下个CI框架的源码都会看到很多的get_instance() 函数,这个函数是用来获取CI 的全局超级对象,CI 是单例模式的框架,所有全局有一个超级对象.因为只有一个实例,所以无论这个函数使用 ...
- 【HDU2037】今年暑假不AC
http://acm.hdu.edu.cn/showproblem.php?pid=2037 “今年暑假不AC?”“是的.”“那你干什么呢?”“看世界杯呀,笨蛋!”“@#$%^&*%...” ...