- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章快速了解Java中ThreadLocal类由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
最近看android framework层代码,看到了threadlocal这个类,有点儿陌生,就翻了各种相关博客一一拜读;自己随后又研究了一遍源码,发现自己的理解较之前阅读的博文有不同之处,所以决定自己写篇文章说说自己的理解,希望可以起到以下作用: - 可以疏通研究结果,加深自己的理解; - 可以起到抛砖引玉的作用,帮助感兴趣的同学疏通思路; - 分享学习经历,同大家一起交流和学习.
1、 threadlocal 是什么 。
threadlocal 是java类库的基础类,在包java.lang下面; 。
官方的解释是这样的: implements a thread-local storage, that is, a variable for which each thread has its own value. all threads share the same threadlocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. the implementation supports null values. 。
大致意思是: 可以实现线程的本地存储机制,threadlocal变量是一个不同线程可以拥有不同值的变量。所有的线程可以共享同一个threadlocal对象,但是不同线程访问的时候可以取得不同的值,而且任意一个线程对它的改变不会影响其他线程。类实现是支持null值的(可以在set和get方法传递和访问null值).
概括来讲有三个特性:
- 不同线程访问时取得不同的值 - 任意线程对它的改变不影响其他线程 - 支持null 。
下面分别对这些特性进行实例验证,首先定义一个test类,在此类中我们鉴证上边所提到的三个特性。类定义如下:
test.java 。
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
|
public
class
test{
//定义threadlocal
private
static
threadlocal name;
public
static
void
main(string[] args)
throws
exception{
name =
new
threadlocal();
//define thread a
thread a =
new
thread(){
public
void
run(){
system.out.println(
"before invoke set,value is:"
+name.get());
name.set(“thread a”);
system.out.println(
"after invoke set, value is:"
+name.get());
}
}
;
//define thread b
thread b =
new
thread(){
public
void
run(){
system.out.println(
"before invoke set,value is :"
+name.get());
name.set(“thread b”);
system.out.println(
"after invoke set,value is :"
+name.get());
}
}
;
// not invoke set, print the value is null
system.out.println(name.get());
// invoke set to fill a value
name.set(“thread main”);
// start thread a
a.start();
a.join();
// print the value after changed the value by thread a
system.out.println(name.get());
// start thread b
b.start();
b.join();
// print the value after changed the value by thread b
system.out.println(name.get())
}
}
|
代码分析:
从定义中我们可以看到只声明了一个threadlocal对象,其他三个线程(主线程、thread a和thread b)共享同一个对象;然后,在不同的线程中修改对象的值和在不同的线程中访问对象的值,并在控制台输出查看结果.
看结果:
从控制台输出结果可以看到里边有三个null的输出,这个是因为在输出前没有对对象进行赋值,验证了支持null的特点;再者,还可以发现在每个线程我都对对象的值做了修改,但是在其他线程访问对象时并不是修改后的值,而是访问线程本地的值;这样也验证了其他两个特点.
2、 threadlocal的作用 。
大家都知道它的使用场景大都是多线程编程,至于具体的作用,这个怎么说那?我觉得这个只能用一个泛的说法来定义,因为一个东西的功能属性定义了以后会限制大家的思路,就好比说菜刀是用来切菜的,好多人就不会用它切西瓜了。 这里,说下我对它的作用的认识,仅供参考,希望能有所帮助。这样来描述吧,当一个多线程的程序需要对多数线程的部分任务(就是run方法里的部分代码)进行封装时,在封装体里就可以用threadlocal来包装与线程相关的成员变量,从而保证线程访问的独占性,而且所有线程可以共享一个封装体对象;可以参考下android里的looper。不会用代码描述问题的程序员不是好程序员; 。
看代码:统计线程某段代码耗时的工具(为说明问题自造) 。
statisticcosttime.java 。
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
|
// class that statistic the cost time
public
class
statisticcosttime{
// record the starttime
// private threadlocal starttime = new threadlocal();
private
long
starttime;
// private threadlocal costtime = new threadlocal();
private
long
costtime;
private
statisticcosttime(){
}
//singleton
public
static
final
statisticcosttime shareinstance(){
return
instancefactory.instance;
}
private
static
class
instancefactory{
private
static
final
statisticcosttime instance =
new
statisticcosttime();
}
// start
public
void
start(){
// starttime.set(system. nanotime ());
starttime = system.nanotime();
}
// end
public
void
end(){
// costtime.set(system. nanotime () - starttime.get());
costtime = system.nanotime() - starttime;
}
public
long
getstarttime(){
return
starttime;
// return starttime.get();
}
public
long
getcosttime(){
// return costtime.get();
return
costtime;
}
|
好了,工具设计完工了,现在我们用它来统计一下线程耗时试试呗:
main.java 。
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
|
public
class
main{
public
static
void
main(string[] args)
throws
exception{
// define the thread a
thread a =
new
thread(){
public
void
run(){
try
{
// start record time
statisticcosttime.shareinstance().start();
sleep(
200
);
// print the start time of a
system.out.println(
"a-starttime:"
+statisticcosttime.shareinstance().getstarttime());
// end the record
statisticcosttime.shareinstance().end();
// print the costtime of a
system.out.println(
"a:"
+statisticcosttime.shareinstance().getcosttime());
}
catch
(exception e){
}
}
}
;
// start a
a.start();
// define thread b
thread b =
new
thread(){
public
void
run(){
try
{
// record the start time of b1
statisticcosttime.shareinstance().start();
sleep(
100
);
// print the start time to console
system.out.println(
"b1-starttime:"
+statisticcosttime.shareinstance().getstarttime());
// end record start time of b1
statisticcosttime.shareinstance().end();
// print the cost time of b1
system.out.println(
"b1:"
+statisticcosttime.shareinstance().getcosttime());
// start record time of b2
statisticcosttime.shareinstance().start();
sleep(
100
);
// print start time of b2
system.out.println(
"b2-starttime:"
+statisticcosttime.shareinstance().getstarttime());
// end record time of b2
statisticcosttime.shareinstance().end();
// print cost time of b2
system.out.println(
"b2:"
+statisticcosttime.shareinstance().getcosttime());
}
catch
(exception e){
}
}
}
;
b.start();
}
}
|
运行代码后输出结果是这样的 注意:输出结果精确度为纳秒级 。
看结果是不是和我们预想的不一样,发现a的结果应该约等于b1+b2才对呀,怎么变成和b2一样了那?答案就是我们在定义starttime和costtime变量时,本意是不应共享的,应是线程独占的才对。而这里变量随单例共享了,所以当计算a的值时,其实starttime已经被b2修改了,所以就输出了和b2一样的结果.
现在我们把statisticcosttime中注释掉的部分打开,换成threadlocal的声明方式试下。 看结果:
呀!这下达到预期效果了,这时候有同学会说这不是可以线程并发访问了吗,是不是只要我用了threadlocal就可以保证线程安全了?答案是no!首先先弄明白为什么会有线程安全问题,无非两种情况: 1、不该共享的资源,你在线程间共享了; 2、线程间共享的资源,你没有保证有序访问; 前者可以用“空间换时间”的方式解决,用threadlocal(也可以直接声明线程局部变量),后者用“时间换空间”的方式解决,显然这个就不是threadlocal力所能及的了.
3、 threadlocal 原理 。
实现原理其实很简单,每次对threadlocal 对象的读写操作其实是对线程的values对象的读写操作;这里澄清一下,没有什么变量副本的创建,因为就没有用变量分配的内存空间来存t对象的,而是用它所在线程的values来存t对象的;我们在线程中每次调用threadlocal的set方法时,实际上是将object写入线程对应values对象的过程;调用threadlocal的get方法时,实际上是从线程对应values对象取object的过程.
看源码:
threadlocal 的成员变量set 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/**
* sets the value of this variable for the current thread. if set to
* {@code null}, the value will be set to null and the underlying entry will
* still be present.
*
* @param value the new value of the variable for the caller thread.
*/
public
void
set(t value) {
thread currentthread = thread.currentthread();
values values = values(currentthread);
if
(values ==
null
) {
values = initializevalues(currentthread);
}
values.put(
this
, value);
}
|
treadlocal 的成员方法get 。
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
|
/**
* returns the value of this variable for the current thread. if an entry
* doesn't yet exist for this variable on this thread, this method will
* create an entry, populating the value with the result of
* {@link #initialvalue()}.
*
* @return the current value of the variable for the calling thread.
*/
@suppresswarnings
(
"unchecked"
)
public
t get() {
// optimized for the fast path.
thread currentthread = thread.currentthread();
values values = values(currentthread);
if
(values !=
null
) {
object[] table = values.table;
int
index = hash & values.mask;
if
(
this
.reference == table[index]) {
return
(t) table[index +
1
];
}
}
else
{
values = initializevalues(currentthread);
}
return
(t) values.getaftermiss(
this
);
}
|
threadlocal的成员方法initializevalues 。
1
2
3
4
5
6
|
/**
* creates values instance for this thread and variable type.
*/
values initializevalues(thread current) {
return
current.localvalues =
new
values();
}
|
threadlocal 的成员方法values 。
1
2
3
4
5
6
|
/**
* gets values instance for this thread and variable type.
*/
values values(thread current) {
return
current.localvalues;
}
|
那这个values又是怎样读写object那?
values是作为threadlocal的内部类存在的;这个values里包括了一个重要数组object[],这个数据就是解答问题的关键部分,它是用来存储线程本地各种类型treadlocal变量用的;那么问题来了,具体取某个类型的变量时是怎么保证不取到其他类型的值那?按一般的做法会用一个map根据key-value映射一下的;对的,思路就是这个思路,但是这里并没有用map来实现,是用一个object[]实现的map机制;但是,若要用map理解的话,也是不可以的,因为机制是相同的;key其实上对应threadlocal的弱引用,value就对应我们传进去的object.
解释下是怎么用object[]实现map机制的(参考图1);它是用数组下标的奇偶来区分key和value的,就是下表是偶数的位置存储key,奇数存储value,就是这样搞得;感兴趣的同学如果想知道算法实现的话,可以深入研究一下,这里我不在详述了.
结合前面第一个实例分析下存储情况:
当程序执行时存在a,b和main三个线程,分别在线程中调用name.set()时同时针对三个线程实例在堆区分配了三块相同的内存空间来存储values对象,以name引用作为key,具体的object作为值存进三个不同的object[](参看下图):
4、 总结 。
threadlocal 不能完全解决多线程编程时的并发问题,这种问题还要根据不同的情况选择不同的解决方案,“空间换时间”还是“时间换空间”.
threadlocal最大的作用就是把线程共享变量转换成线程本地变量,实现线程之间的隔离.
以上就是本文关于快速了解java中threadlocal的全部内容,希望对大家有所帮助。如有不足之处,欢迎留言指出。感谢朋友们对本站的支持.
原文链接:https://www.2cto.com/kf/201608/541771.html 。
最后此篇关于快速了解Java中ThreadLocal类的文章就讲到这里了,如果你想了解更多关于快速了解Java中ThreadLocal类的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!