- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章深入解析Java中ThreadLocal线程类的作用和用法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
ThreadLocal与线程成员变量还有区别,ThreadLocal该类提供了线程局部变量。这个局部变量与一般的成员变量不一样,ThreadLocal的变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本,这是Java API中的描述,通过阅读API源码,发现并非副本,副本什么概念?克隆品? 或者是别的样子,太模糊。 准确的说,应该是ThreadLocal类型的变量内部的注册表(Map<Thread,T>)发生了变化,但ThreadLocal类型的变量本身的确是一个,这才是本质! 下面就做个例子: 1、标准例子 定义了MyThreadLocal类,创建它的一个对象tlt,分别给四个线程使用,结果四个线程tlt变量并没有出现共用现象,二是各用各的,这说明,四个线程使用的是tlt的副本(克隆品).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/**
* 使用了ThreadLocal的类
*/
public
class
MyThreadLocal {
//定义了一个ThreadLocal变量,用来保存int或Integer数据
private
ThreadLocal<Integer> tl =
new
ThreadLocal<Integer>() {
@Override
protected
Integer initialValue() {
return
0
;
}
};
public
Integer getNextNum() {
//将tl的值获取后加1,并更新设置t1的值
tl.set(tl.get() +
1
);
return
tl.get();
}
}
|
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 测试线程
*/
public
class
TestThread
extends
Thread {
private
MyThreadLocal tlt =
new
MyThreadLocal();
public
TestThread(MyThreadLocal tlt) {
this
.tlt = tlt;
}
@Override
public
void
run() {
for
(
int
i =
0
; i <
3
; i++) {
System.out.println(Thread.currentThread().getName() +
"\t"
+ tlt.getNextNum());
}
}
}
|
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* ThreadLocal测试
*/
public
class
Test {
public
static
void
main(String[] args) {
MyThreadLocal tlt =
new
MyThreadLocal();
Thread t1 =
new
TestThread(tlt);
Thread t2 =
new
TestThread(tlt);
Thread t3 =
new
TestThread(tlt);
Thread t4 =
new
TestThread(tlt);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
|
可以看出,三个线程各自独立编号,互不影响:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Thread-0 1
Thread-1 1
Thread-0 2
Thread-1 2
Thread-0 3
Thread-1 3
Thread-2 1
Thread-3 1
Thread-2 2
Thread-3 2
Thread-2 3
Thread-3 3
Process finished with exit code 0
|
tlt对象是一个,废话tl对象也是一个,因为组合关系是一对一的。但是tl对象内部的Map随着线程的增多,会创建很多Integer对象。只是Integer和int已经通用了。所以感觉不到Integer的对象属性。 2、不用ThreadLocal 假如不用ThreadLocal,只需要将MyThreadLocal类重新定义为:
1
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/**
* 使用了ThreadLocal的类
*/
public
class
MyThreadLocal {
private
Integer t1 =
0
;
public
Integer getNextNum(){
return
t1=t1+
1
;
}
// 定义了一个ThreadLocal变量,用来保存int或Integer数据
// private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
// @Override
// protected Integer initialValue() {
// return 0;
// }
// };
//
// public Integer getNextNum() {
// //将tl的值获取后加1,并更新设置t1的值
// tl.set(tl.get() + 1);
// return tl.get();
// }
}
|
然后运行测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Thread-2 1
Thread-2 2
Thread-1 4
Thread-1 6
Thread-3 3
Thread-3 9
Thread-3 10
Thread-1 8
Thread-0 7
Thread-0 11
Thread-0 12
Thread-2 5
Process finished with exit code 0
|
从这里可以看出,四个线程共享了tlt变量,结果每个线程都直接修改tlt的属性。 3、自己实现个ThreadLocal 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package
com.lavasoft.test2;
import
java.util.Collections;
import
java.util.HashMap;
import
java.util.Map;
/**
* 使用了ThreadLocal的类
*/
public
class
MyThreadLocal {
//定义了一个ThreadLocal变量,用来保存int或Integer数据
private
com.lavasoft.test2.ThreadLocal<Integer> tl =
new
com.lavasoft.test2.ThreadLocal<Integer>() {
@Override
protected
Integer initialValue() {
return
0
;
}
};
public
Integer getNextNum() {
//将tl的值获取后加1,并更新设置t1的值
tl.set(tl.get() +
1
);
return
tl.get();
}
}
class
ThreadLocal<T> {
private
Map<Thread, T> map = Collections.synchronizedMap(
new
HashMap<Thread, T>());
public
ThreadLocal() {
}
protected
T initialValue() {
return
null
;
}
public
T get() {
Thread t = Thread.currentThread();
T obj = map.get(t);
if
(obj ==
null
&& !map.containsKey(t)) {
obj = initialValue();
map.put(t, obj);
}
return
obj;
}
public
void
set(T value) {
map.put(Thread.currentThread(), value);
}
public
void
remove() {
map.remove(Thread.currentThread());
}
}
|
运行测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Thread-0 1
Thread-0 2
Thread-0 3
Thread-2 1
Thread-2 2
Thread-3 1
Thread-2 3
Thread-3 2
Thread-1 1
Thread-3 3
Thread-1 2
Thread-1 3
Process finished with exit code 0
|
很意外,这个山寨版的ThreadLocal也同样运行很好,实现了JavaAPI中ThreadLocal的功能。 4、透过现象看本质 其实从程序角度看,tlt变量的确是一个,毫无疑问的。但是为什么打印出来的数字就互不影响呢? 是因为使用了Integer吗?-----不是。 原因是:protected T initialValue()和get(),因为每个线程在调用get()时候,发现Map中不存在就创建。调用它的时候,就创建了一个新变量,类型为T。每次都新建,当然各用个的互不影响了。 为了看清本质,将Integer换掉,重写部分类: 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
package
com.lavasoft.test2;
import
java.util.Collections;
import
java.util.HashMap;
import
java.util.Map;
/**
* 使用了ThreadLocal的类
*/
public
class
MyThreadLocal {
//定义了一个ThreadLocal变量,用来保存int或Integer数据
// private ThreadLocal<Bean> tl = new ThreadLocal<Bean>() {
private
com.lavasoft.test2.ThreadLocal<Bean> tl =
new
com.lavasoft.test2.ThreadLocal<Bean>() {
@Override
protected
Bean initialValue() {
return
new
Bean();
}
};
@Override
public
String toString() {
return
"MyThreadLocal{"
+
"tl="
+ tl +
'}'
;
}
public
Bean getBean() {
return
tl.get();
}
}
class
ThreadLocal<T> {
private
Map<Thread, T> map = Collections.synchronizedMap(
new
HashMap<Thread, T>());
public
ThreadLocal() {
}
protected
T initialValue() {
return
null
;
}
public
T get() {
Thread t = Thread.currentThread();
T obj = map.get(t);
if
(obj ==
null
&& !map.containsKey(t)) {
obj = initialValue();
map.put(t, obj);
}
return
obj;
}
public
void
set(T value) {
map.put(Thread.currentThread(), value);
}
public
void
remove() {
map.remove(Thread.currentThread());
}
}
|
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package
com.lavasoft.test2;
/**
* 测试Bean
*/
public
class
Bean {
private
String id =
"0"
;
private
String name =
"none"
;
public
Bean() {
}
public
Bean(String id, String name) {
this
.id = id;
this
.name = name;
}
public
String getId() {
return
id;
}
public
void
setId(String id) {
this
.id = id;
}
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
String showinfo() {
return
"Bean{"
+
"id='"
+ id + '\
''
+
", name='"
+ name + '\
''
+
'}'
;
}
}
|
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package
com.lavasoft.test2;
/**
* 测试线程
*/
public
class
TestThread
extends
Thread {
private
MyThreadLocal tlt =
new
MyThreadLocal();
public
TestThread(MyThreadLocal tlt) {
this
.tlt = tlt;
}
@Override
public
void
run() {
System.out.println(
">>>>>:"
+ tlt);
for
(
int
i =
0
; i <
3
; i++) {
System.out.println(Thread.currentThread().getName() +
"\t"
+tlt.getBean()+
"\t"
+tlt.getBean().showinfo());
}
}
}
|
然后运行测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
Thread-1 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'}
Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id='0', name='none'}
Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id='0', name='none'}
Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id='0', name='none'}
Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id='0', name='none'}
Thread-0 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'}
Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id='0', name='none'}
Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id='0', name='none'}
Thread-1 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'}
Thread-0 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'}
Thread-0 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'}
Thread-1 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'}
Process finished with exit code 0
|
从打印结果很清楚的看到,MyThreadLocal的tlt对象的确是一个,tlt对象里的ThreadLocal的tl对象也是一个,但是,将t1t给每个线程用的时候,线程会重新创建Bean对象加入到ThreadLocal的Map中去使用.
关于ThreadLocal的几个误区: 1、ThreadLocal是java线程的一个实现 。
ThreadLocal的确是和java线程有关,不过它并不是java线程的一个实现,它只是用来维护本地变量。针对每个线程,提供自己的变量版本,主要是为了避免线程冲突,每个线程维护自己的版本。彼此独立,修改不会影响到对方.
2、ThreadLocal是相对于每个session的 。
ThreadLocal顾名思义,是针对线程。在java web编程上,每个用户从开始到会话结束,都有自己的一个session标识。但是ThreadLocal并不是在会话层上。其实,Threadlocal是独立于用户session的。它是一种服务器端行为,当服务器每生成一个新的线程时,就会维护自己的ThreadLocal.
对于这个误解,个人认为应该是开发人员在本地基于一些应用服务器测试的结果。众所周知,一般的应用服务器都会维护一套线程池,也就是说,对于每次访问,并不一定就新生成一个线程。而是自己有一个线程缓存池。对于访问,先从缓存池里面找到已有的线程,如果已经用光,才去新生成新的线程.
所以,由于开发人员自己在测试时,一般只有他自己在测,这样服务器的负担很小,这样导致每次访问可能是共用同样一个线程,导致会有这样的误解:每个session有一个ThreadLocal 。
3、ThreadLocal是相对于每个线程的,用户每次访问会有新的ThreadLocal 。
理论上来说,ThreadLocal是的确是相对于每个线程,每个线程会有自己的ThreadLocal。但是上面已经讲到,一般的应用服务器都会维护一套线程池。因此,不同用户访问,可能会接受到同样的线程。因此,在做基于TheadLocal时,需要谨慎,避免出现ThreadLocal变量的缓存,导致其他线程访问到本线程变量 。
4、对每个用户访问,ThreadLocal可以多用 。
可以说,ThreadLocal是一把双刃剑,用得来的话可以起到非常好的效果。但是,ThreadLocal如果用得不好,就会跟全局变量一样。代码不能重用,不能独立测试。因为,一些本来可以重用的类,现在依赖于ThreadLocal变量。如果在其他没有ThreadLocal场合,这些类就变得不可用了。个人觉得ThreadLocal用得很好的几个应用场合,值得参考 。
1、存放当前session用户:quake want的jert 。
2、存放一些context变量,比如webwork的ActionContext 。
3、存放session,比如Spring hibernate orm的session 。
最后此篇关于深入解析Java中ThreadLocal线程类的作用和用法的文章就讲到这里了,如果你想了解更多关于深入解析Java中ThreadLocal线程类的作用和用法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我一直在读这个article关于 Tomcat 中的 ThreadLocal 泄漏。第一个示例包含以下代码: public class MyCounter { private int
天真地,我希望 ThreadLocal 是 Thread 到值类型的某种 WeakHashMap。所以当我了解到一个 ThreadLocal 的值实际上是 saved in a map in the
一 简单例子 1 代码 package concurrent.threadlocal; /** * ThreadLocal测试 * * @author cakin */ public class T
并发编程-ThreadLocal 说在前面的话 今天的文章很短,但是很经典,值得你仔细阅读每一个文字… 正如我开篇所说,我们要整理一些java并发编程的学习文档,这一篇就是第七篇:ThreadLoca
最近接了一个新需求,业务场景上需要在原有基础上新增2个字段,接口新增参数意味着很多类和方法的逻辑都需要改变,需要先判断是否属于该业务场景,再做对应的逻辑。原本的打算是在入口处新增变量,在操作数据的时
前言 ThreadLocal为变量在每个线程中都创建了一个副本,所以每个线程可以访问自己内部的副本变量,不同线程之间不会互相干扰。本文会基于实际场景介绍ThreadLocal如何使用以及内部实现机
来源:blog.csdn.net/mycs2012/article/details/90898128 1、FastThreadLocal的引入背景和原理简介 2、实现源码分析 2.1、Unpadded
来源:blog.csdn.net/mycs2012/article/details/90898128 1、FastThreadLocal的引入背景和原理简介 2、实现源码分析 2.1、Unpadded
.Net 4. ThreadLocal<> 实现 IDisposable。但似乎调用 Dispose() 实际上并没有释放对所持有的线程本地对象的引用。 这段代码重现了这个问题: using Syst
在类(class)ReentrantReadWriteLock以下是奇怪的评论: transient ThreadLocalHoldCounter readHolds; Sync() { re
如果我们有一个 ThreadLocal 属性(每个线程都有其唯一的属性),那么哪个是正确的(我们不想使用自动 setter/getter): A) private ThreadLocal _someP
我正在浏览 ThreadLocal类文档,想知道它可以在什么场景下使用。 首先我认为它可以用于那些我们有第三方/遗留类并且我们想要处理同步问题的场景。然后我查看了 ThreadLocal 的其他示例,
我最近读了一篇关于 Equation Group's Sophisticated Hacking 的文章确凿的证据是一个常量,也出现在 JDK 8 源代码中,例如ThreadLocal.java HA
我的基于 ThreadLocal 的类遇到问题。任何帮助,将不胜感激。这是一个带有简单列表的基类: public class ThreadLocalTest { protected static fi
使用ThreadLocal类编程时,字段应该声明为final吗?如果编写类似 private ThreadLocal threadLocal 的代码稍后在构造函数中初始化它,因为变量 threadLo
我有以下类(class) 这个类用来保存我所有的ThreadLocal数据成员 public class ThreadLocalManager { public static final Th
在一次工作 session 上。我听说Thread Local绝对是一种反模式,因为新的应用程序服务器使用称为新IO的新线程技术。事实上,他们告诉我ThreadLocal的问题是一个完整的线程必须等待
ThreadLocal 是否会自动清除为已完成的线程创建的值? 最佳答案 是的,这些变量可用于垃圾收集,但前提是没有其他对这些值的引用(由其他线程持有)。但是当你说一个线程完成时,它不应该像一个线程池
我意识到 ThreadLocal 已被多次访问,尤其是 SimpleDateFormat 示例。 但似乎即使将 SDF 设置为“ThreadLocal”,我们仍然为每个线程创建一个 SDF() 实例,
在 JSR 315 中添加了对 servlet 和过滤器的异步支持。在这样的过滤器中创建的 ThreadLocal 线程安全吗? ResourceFilter com.app.fil
我是一名优秀的程序员,十分优秀!