谈话Java在ThreadLocal理解类
我们必须先了解:ThreadLocal不超过一个线程类,或者它应该被称为线程局部变量。这从ThreadLocal的JDK我们可以看到的定义
public class ThreadLocal<T>extends Object
能够看出ThreadLocal仅仅是一个普普通通的类,并没有继承自Thread或实现Runnable接口。
同一时候也能够看到ThreadLocal使用了泛型。这样他就能够操作差点儿不论什么类型的数据了。
以下说JDK API代码时详细再说。
对此类,看看JDK API中的部分描写叙述:
该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通相应物,由于訪问某个变量(通过其 get 或 set 方法)的每一个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例一般是类中的 private static 字段,它们希望将状态与某一个线程(比如,用户 ID 或事务 ID)相关联。
1、ThreadLocal不是线程。而是县城的一个局部变量
2、再使用时ThreadLocal通常作为类中的一个private static变量使用
3、每一个线程都有一个自己的局部变量,之间互不冲突,对变量值的操作互不影响,不存在资源冲突。
整体来说。ThreadLocal作为线程的工具,为每个线程独立的提供了一个存储与訪问线程变量的方式,使得不同线程间存储的数据互不影响(因此不会出现资源竞争问题)。
以下就说说ThreadLocal是怎样作为一个线程工具,为每一个线程提供存储与訪问线程局部变量的。
ThreadLocal提供了下面几个API用于存取线程局部变量:
T |
get() 返回此线程局部变量的当前线程副本中的值。 |
protected T |
initialValue() 返回此线程局部变量的当前线程的初始值。 |
void |
remove() 移除此线程局部变量的值。 |
void |
set(T value)将此线程局部变量的当前线程副本中的值设置为指定值。 |
能够看到每一个都是泛型类型的。
接口的工作原理以下会详细介绍。
ThreadLocal类中还提供了一个静态的内部类:
static class ThreadLocalMap
我们能够把这个ThreadLocalMap看做是一个普通的MAP类型,能够保存一个键值对就可以,这样便于理解。
这个ThreadLocalMap在ThreadLocal中定义,但却在线程类java.lang.Thread中使用。在Thread类中有下面定义:
ThreadLocal.ThreadLocalMap threadLocals = null
言外之意就是Thread类中有一个类似于Map的ThreadLocal.ThreadLocalMap 类型的变量,能够用于存储键值对类型的值。
以下上一个简单的样例。依据输出结果。分析Thread与ThreadLocal工作过程。
类SerialNum
public class SerialNum {
// The next serial number to be assigned
private static int nextSerialNum = 0;
public static ThreadLocal serialNum = new ThreadLocal() {
protected synchronized Object initialValue() {
return new Integer(nextSerialNum++);
}
};
public static int get() {
return ((Integer) (serialNum.get())).intValue();
}
public static void set(int val){
serialNum.set(val);
}
}
类SerialNum中定义了一个private static ThreadLocal 类型的匿名内部类serialNum变量,事实上全然能够直接new ThreadLocal,这样写仅仅是为了实现重写其initialValue方法。由于在ThreadLocal中initialValue()方法返回一个NULL值。须要说明的是,initialValue()方法是一个懒载入方法,被ThreadLocal类内部调用。而且仅仅在第一次threadLocal类的get()被调用时调用。重写它是为了让局部变量有一个初始值。
SerialNum类的静态get()和set()方法都调用ThreadLocal类的get()和set()方法。
类:ThreadTest
public class ThreadTest extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
//先打印初始值
System.out.println(Thread.currentThread().getName()+":"+SerialNum.get());
//当前值加4
SerialNum.set(SerialNum.get()+4);
//打印加4后的值
System.out.println(Thread.currentThread().getName()+":"+SerialNum.get());
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadTest t = new ThreadTest();
ThreadTest t2 = new ThreadTest();
ThreadTest t3 = new ThreadTest();
t.start();
t2.start();
t3.start();
}
}
ThreadTest中的run方法中调用了SerialNum类的静态get()和set()方法。
输出结果例如以下:
Thread-2:2
Thread-0:0
Thread-1:1
Thread-2:6
Thread-0:4
Thread-1:5
以下我们開始分析为什么会出现这种结果。我们就拿线程Thread-0来说,它相应的就是t.start()启动的线程
1、当t线程先执行时(thread-0),会调用SerialNum.get(),而其会再调用threadLocal的get方法。
ThreadLocal的get()方法例如以下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
return (T)map.get(this);
// Maps are constructed lazily. if the map for this thread
// doesn't exist, create it, with this ThreadLocal and its
// initial value as its only entry.
T value = initialValue();
createMap(t, value);
return value;
}
get()方法中先获得当前线程,然后调用getMap(),API方法例如以下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
getMap()返回当前线程中的 threadLocals变量。前面我们说过,Thread中定义了一个ThreadLocal.ThreadLocalMap类型的变量。就是这个了。
然后在get()方法中。检查map为不为NUll。假设是NULL值,所以会调用initialValue()。而这种方法被我们重写了。它返回了SerialNum类中定义的静态变量nextSerialNum当前值,然后将nextSerialNum+1。由于nextSerialNum初始为0,所以返回的是0。然后调用createMap(t,value)。
这个API内容例如以下:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
能够看到 createMap中创建了一个ThreadLoadMap对象。并复制给当前线程的threadLocals变量。新创建的ThreadLocalMap类中保存了局部变量的值value,当中键为this,即我们在SerialNum类中定义的那个private static ThreadLocal serialNum。
最后get()方法返回初始化好的value值0。
同一时候
以上就是thead-0第一次输出打印出0的原因。
2、当线程thread-0执行到SerialNum.set(SerialNum.get()+4);这一句时,我们看发生了什么
首先SerialNum.get()+4会取出threadLocal变量serialNum中保存的线程局部变量值,回头看看ThreadLocal.get()方法的介绍,由于此时当前线程中已经有了ThreadLocalMap 类型的threadLocals,所以会直接调用map.get(this);当中this依旧是我们在SerialNum类中定义的那个private static ThreadLocal serialNum。
这样就返回了map中的值。
然后+4后调用ThreadLocal的set()方法。我们看看set()方法的JDK内容:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
能够看到set()方法首先也是得到当前线程,从当前线程中获取threadLocalMap类型的map。假设map不为null,就将value保存进去。假设为null。就在createMap中new一个ThreadLocalMap,保存value。然后将这个 新new的threadLocalMap类型的对象赋值给当前线程的threadLocals变量(详见createMap()方法)。
经过执行后,局部变量值就等于0+4=4了。
这也是为什么第二次打印出的值为thread-0:4。
再简单说说第二个线程thread-1。第二个线程启动时,由于静态变量nextSerialNum已经为1(被线程thread-0读取后++)。所以他是从1開始打印。而且后面的+4操作与线程thread-0和thread-2并不冲突。由于:
thread-0这个线程的局部变量被保存进threadLocalMap后,这个threadLocalMap变量被复制给这个线程(也就是thread-0自己)的threadLocals变量了。相同的。thread-1;thread-2这两个线程的局部变量保存进threadLocalMap后,都分别复制给自己这个线程的threadLocals变量了。所以以后再操作都是从自己线程里面取threadLocalMap。当然互不影响。
相同的一点是这三个线程的threadLocalMap中的KEY都为同一个ThreadLocal对象。
通过这个样例还能看出一点,局部变量初始化后,就再和開始那个nextSerialNum没关系了。这应该就是JDK API中描写叙述的:訪问一个变量(通过其
get 或 set 方法)的每一个线程都有自己的局部变量,它独立于变量的初始化副本
以上就是自己对ThreadLocal使用的浅谈。開始怕说不清楚。就想写的直白点。后来自己都感觉啰嗦了
。
版权声明:本文博客原创文章。博客,未经同意,不得转载。
谈话Java在ThreadLocal理解类的更多相关文章
- 【Java】深入理解ThreadLocal
一.前言 要理解ThreadLocal,首先必须理解线程安全.线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位.当程序以单线程运行的时候,我们不需要考虑线程安全.然而当一个进程中包 ...
- java中ThreadLocal类的使用
ThreadLocal是解决线程安全问题一个很好的思路,ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本,由于Key值不可重复, ...
- java中threadlocal的理解
[TOC] #java中threadlocal的理解##一.threadlocal的生命周期和ThreadLocalMap的生命周期可以吧TreadLocal看做是一个map来使用,只不过这个map是 ...
- java的ThreadLocal类的使用方法
java的ThreadLocal类的使用方法,ThreadLocal是一个支持泛型的类,用在多线程中用于防止并发冲突问题. 比如以下的一个样例,就是用于线程添加1,可是相互不冲突 package co ...
- 深入研究java.lang.ThreadLocal类 (转)
深入研究java.lang.ThreadLocal类 一.概述 ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thr ...
- 《深入理解java虚拟机》:类的初始化
深入理解java虚拟机>:类的初始化 类从被载入到虚拟机内存中開始.到卸载出内存为止,它的整个生命周期包含:载入.验证.准备.解析.初始化.使用和卸载七个阶段.当中验证.准备.解析3个部分统称为 ...
- Java多线程——ThreadLocal类的原理和使用
Java多线程——ThreadLocal类的原理和使用 摘要:本文主要学习了ThreadLocal类的原理和使用. 概述 是什么 ThreadLocal可以用来维护一个变量,提供了一个ThreadLo ...
- Java 中 ThreadLocal 内存泄露的实例分析
前言 之前写了一篇深入分析 ThreadLocal 内存泄漏问题是从理论上分析ThreadLocal的内存泄漏问题,这一篇文章我们来分析一下实际的内存泄漏案例.分析问题的过程比结果更重要,理论结合实际 ...
- Java中ThreadLocal无锁化线程封闭实现原理
虽然现在可以说很多程序员会用ThreadLocal,但是我相信大多数程序员还不知道ThreadLocal,而使用ThreadLocal的程序员大多只是知道其然而不知其所以然,因此,使用ThreadLo ...
随机推荐
- SharePoint综合Excel数据与Excel Web Access Web部分
SharePoint综合Excel数据与Excel Web Access Web部分 Excel Web Access Web零件SharePoint于Excel以电子形式提交数据. 1. 打开Exc ...
- android 4.0 中出错 java.lang.UnsupportedOperationException
在android4.0中 画图的时候使用: canvas.clipPath(path, Region.Op.XOR); 报错 java.lang.UnsupportedOperationExcept ...
- 在 树莓派上使用 c++ libsockets library
rpi默认安装的编译器是gcc-4.6.2 而现在最新的c++ libsockets library 需要使用支持c++-11特征的编译器,即需要4.8.2才可以.为此,需要先升级编译器才可以支持编译 ...
- Matlab强迫症产生的图像
最近流行的网络迷恋的照片做头像,闲来无事,取matlab获取一个建设者,它可以产生包括0-9以及99+OCD. 原理很easy,图叠加,这里为了降低文件,将数字图片保存在.mat二进制文件里. === ...
- Windows Phone 8.1 新功能 - 应用栏控件
2014年4月3日的微软Build 2014 大会上.Windows Phone 8.1 正式公布. 相较于Windows Phone 8.不论从用户还是开发人员的角度,都产生了非常大的变化. 接下来 ...
- cnBlog 的windows live writer 客户端配置
重装系统后总是忘,备个档 CNBLOG 博客名 cnblog 日志帐户 http://www.cnblogs.com/liulaocai2/ 用户:359444066 密码:同QQ密码,比QQ少一位 ...
- 在 VS 类库项目中 Add Service References 和 Add Web References 的区别
原文:在 VS 类库项目中 Add Service References 和 Add Web References 的区别 出身问题: 1.在vs2005时代,Add Web Reference(添加 ...
- 【SQL】Oracle的PL/SQL语法及其拓展数据类型总结
PL/SQL语法 PL/SQL程序由三部分组成,声明部分.执行部分.异常处理部分. 模板: DECLARE /*变量声明*/ BEGIN /*程序主体*/ EXCEPTION /*异常处理部分*/ E ...
- UML 简单的总结
上某一个地方,总有个记忆挥不散,每一个深夜某一个地方,总有着最深的思量- 都说岁月无情人有情,记忆easy催人老,可有时候反倒觉着人比岁月更无情.岁月留下了我们成长的印记,但是有时候以前认为会相伴永远 ...
- Android - 用Fragments实现动态UI - 创建灵活的UI
当设计程序来支持各种不一样的屏幕尺寸时,可以在不同的布局中重用fragment来根据可用的屏幕大小来优化用户体验. 例如,在手机上可能使用一个fragment来使用单窗口用户体验比较合适.但是,你可能 ...