当前位置: 首页 > 编程日记 > 正文

ThreadLocal的使用方法

ThreadLocal的含义是Thread Local Variable,它可以声明一个字段,使得不同的线程访问这个字段时,获取的都是不同的副本,互不影响。
ThreadLocal的作用和在每个Thread类声明一个字段相同,那么什么时候使用它呢?还是在编写一些框架时,因为这时你无法预先定义Thread类。其中一个典型的用法是调用一个静态方法,这个静态方法会操作一个ThreadLocal变量,而不同的线程去调用时,访问的就是不同的副本。

下面通过这样一个例子来说明:模拟一个游戏,预先随机设定一个[1, 10]的整数,然后每个玩家去猜这个数字,每个玩家不知道其他玩家的猜测结果,看谁用最少的次数猜中这个数字。

这个游戏确实比较无聊,不过这里恰好可以把每个玩家作为一个线程,然后用ThreadLocal来记录玩家猜测的历史记录,这样就很容易理解ThreadLocal的作用。

Judge:用来设定目标数字以及判断猜测的结果。
Player:每个Player作为一个线程,多个Player并行地去尝试猜测,猜中时线程终止。
Attempt:具有ThreadLocal字段和猜测动作静态方法的类,ThreadLocal用于保存猜过的数字。
Record:保存历史记录的数据结构,有一个List<Integer>字段。

ThreadLocal为了可以兼容各种类型的数据,实际的内容是再通过set和get操作的对象,详见Attempt的getRecord()。
运行的时候,每个Player Thread都是去调用Attemp.guess()方法,进而操作同一个ThreadLocal变量history,但却可以保存每个线程自己的数据,这就是ThreadLocal的作用。

import java.util.ArrayList; import java.util.List; import java.util.Random;public class ThreadLocalTest { public static void main(String[] args) { Judge.prepare(); new Player(1).start(); new Player(2).start(); new Player(3).start(); } }class Judge { public static int MAX_VALUE = 10; private static int targetValue; public static void prepare() { Random random = new Random(); targetValue = random.nextInt(MAX_VALUE) + 1; } public static boolean judge(int value) { return value == targetValue; } }class Player extends Thread { private int playerId; public Player(int playerId) { this.playerId = playerId; } @Override public void run() { boolean success = false; while(!success) { int value = Attempt.guess(Judge.MAX_VALUE); success = Judge.judge(value); System.out.println(String.format("Plyaer %s Attempts %s and %s", playerId, value, success ? " Success" : "Failed")); } Attempt.review(String.format("[IFNO] Plyaer %s Completed by ", playerId)); } }class Attempt { private static ThreadLocal<Record> history = new ThreadLocal<Record>(); public static int guess(int maxValue) { Record record = getRecord(); Random random = new Random(); int value = 0; do { value = random.nextInt(maxValue) + 1; } while (record.contains(value)); record.save(value); return value; } public static void review(String info) { System.out.println(info + getRecord()); } private static Record getRecord() { Record record = history.get(); if(record == null) { record = new Record(); history.set(record); } return record; } }class Record { private List<Integer> attemptList = new ArrayList<Integer>();; public void save(int value) { attemptList.add(value); } public boolean contains(int value) { return attemptList.contains(value); } @Override public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append(attemptList.size() + " Times: "); int count = 1; for(Integer attempt : attemptList) { buffer.append(attempt); if(count < attemptList.size()) { buffer.append(", "); count++; } } return buffer.toString(); } }


运行结果

Plyaer 2 Attempts 8 and Failed Plyaer 3 Attempts 6 and Failed Plyaer 1 Attempts 5 and Failed Plyaer 2 Attempts 7 and Success Plyaer 3 Attempts 9 and Failed Plyaer 1 Attempts 9 and Failed Plyaer 3 Attempts 2 and Failed Plyaer 1 Attempts 2 and Failed [IFNO] Plyaer 2 Completed by 2 Times: 8, 7 Plyaer 3 Attempts 4 and Failed Plyaer 1 Attempts 1 and Failed Plyaer 3 Attempts 5 and Failed Plyaer 1 Attempts 3 and Failed Plyaer 3 Attempts 1 and Failed Plyaer 1 Attempts 10 and Failed Plyaer 3 Attempts 8 and Failed Plyaer 1 Attempts 6 and Failed Plyaer 3 Attempts 7 and Success Plyaer 1 Attempts 4 and Failed [IFNO] Plyaer 3 Completed by 8 Times: 6, 9, 2, 4, 5, 1, 8, 7 Plyaer 1 Attempts 7 and Success [IFNO] Plyaer 1 Completed by 9 Times: 5, 9, 2, 1, 3, 10, 6, 4, 7


关于ThreadLocal的原理,可以从其get()方法的实现来看

public class ThreadLocal<T> {

...

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 getMap(Thread t) { return t.threadLocals; }

...

}


执行get()时首先获取当前的Thread,再获取Thread中的ThreadLocalMap - t.threadLocals,并以自身为key取出实际的value。于是可以看出,ThreadLocal的变量实际还是保存在Thread中的,容器是一个Map,Thread用到多少ThreadLocal变量,就会有多少以其为key的Entry。


转自:http://gaofeihang.blog.163.com/blog/static/84508285201293071940706/

相关文章:

如何实现对象交互

在本篇随笔中&#xff0c;我们学习下什么是对象选择&#xff0c;投影和反投影是如何工作的&#xff0c;怎样使用Three.js构建可使用鼠标和对象交互的应用。例如当鼠标移到对象&#xff0c;对象变成红色&#xff0c;鼠标移走&#xff0c;对象又恢复原来的颜色。 本篇随笔的源代码…

Matlab与线性代数 -- 矩阵的大小

本图文介绍了如何利用Matlab求矩阵的大小。

最近做了一个小小的系统,收获挺大的....我想总结一下

首先我要感谢老许,是他给了我这次机会.以后我会把我的经验一点一点总结出来....为那些在编程之路上的迷茫者找到方向活着让人兴奋...总觉的应该去做点什么做工程开发吧----我的老师阿温说过一句话让我记忆尤新:"坚持成就传奇".我想用他去勉力每一个在人生路上奋斗的人…

Hibernate和iBATIS 优缺点比较

选择Hibernate还是iBATIS都有它的道理&#xff1a;Hibernate的特点&#xff1a;Hibernate功能强大&#xff0c;数据库无关性好&#xff0c;O/R映射能力强&#xff0c;如果你对Hibernate相当精通&#xff0c;而且对Hibernate进行了适当的封装&#xff0c;那么你的项目整个持久层…

Matlab与线性代数 -- 矩阵的秩

本图文详细介绍了利用Matlab求矩阵秩的方法。

iOS开发 最近开发了蓝牙模块,在此记录总结一下

为什么80%的码农都做不了架构师&#xff1f;>>> 1.基本概念 <1>中心者模式&#xff1a;常用的&#xff08;其实99.99%&#xff09;就是使用中心者模式作为开发&#xff0c;就是我们手机作为主机&#xff0c;连接蓝牙外设。由于开发只用到了中心者模式&#x…

asp.net实现在网页上自动显示超链接以及Email地址

人们总喜欢在帖子中加上各种有用的URL链接或Email地址。而笔者当初设计时没有考虑到这一点&#xff0c;使得这些URL链接或Email地址只能以文字的形式而并不是以超链接的形式显示&#xff0c;其它浏览帖子的人还必须把这些URL链接拷贝到浏览器中或把Email地址拷贝到Outlook中才能…

用开放地址法中的线性探查法解决冲突实现哈希表的运算

为了更深的理解哈希算法&#xff0c;自己写了用开放地址法中的线性探查法解决冲突实现哈希表的运算。 /*** Created by lirui on 14-8-13.* 用开放地址法中的线性探查法解决冲突实现哈希表的运算。*/ public class MyHashSearch {public static final int SIZE 10;public sta…

Re: 求助:5道算法题

http://www.newsmth.net/frames.html发信人: cutepig (cutepig), 信区: Algorithm标 题: 求助&#xff1a;5道算法题发信站: 水木社区 (Sat Nov 10 18:25:06 2007), 站内1)given a integer, output its previous and next neighbor number which has the same number of bit 1…

Linux下des对称性加密

最近对接公安审计一些经历 对方的需求&#xff1a; 打成zip包对zip包进行des-cbc对称性加密&#xff0c;使用约定好的 -K和-iv值比如 -K "abcd$#!" -iv "efgh$#!"加密后做base64编码起初是想尝试用 php 去做&#xff0c;经过一阵折腾之后发现&#xff0c;p…

在软件中常用的“撤销”操作,其本质是“栈”!

本文介绍了栈的定义与操作并利用顺序表和链表实现了栈这种常用的数据结构。

用拉链法实现哈希算法的运算

package lirui.find;import java.util.LinkedList;/*** Created by lirui on 14-8-13.* 用拉链法实现哈希算法的运算*/ public class MyHashSearch2 {public static final int SIZE 10;public static MyHashElement[] hashtable new MyHashElement[SIZE];// 记录hash表中的数…

C#图片处理常见方法性能比较

在.NET编程中&#xff0c;由于GDI的出现&#xff0c;使得对于图像的处理功能大大增强。在文通过一个简单黑白处理实例介绍在.NET中常见的图片处理方法和原理并比较各种方法的性能。 黑白处理原理&#xff1a;彩色图像处理成黑白效果通常有3种算法&#xff1b; (1).最大值法: 使…

软件中常用的“发送邮件”、“打印文档”,其本质是“队列”!

本图文详细介绍了顺序队列、循环队列、链队列的实现过程。

二分查找的循环实现和递归实现

自己实现了二分查找的循环实现和递归实现 说明&#xff1a;二分查找适用于顺序存储结构&#xff0c;不适于链式存储结构&#xff0c;是一个高效的查找方法。虽然折半查找效率高&#xff0c;但是要排序&#xff0c;排序本身是一种很费时的运算。要求传入的表是有序的。二分查找的…

CentOS6.8 编译安装LNMP

思路&#xff1a;根据Linux系统以及公司网站系统的信息&#xff0c;选择合适的安装包进行安装 一、查看系统信息 # uname -a # 查看内核/操作系统/CPU信息 # head -n 1 /etc/issue # 查看操作系统版本 # grep MemTotal /proc/meminfo # 查看内…

js入门·循环与判断/利用函数的简单实例/使用对象/列举对象属性的名称

1,列举对象属性的名称<script language"javascript">varobjnewObject();obj.a"您好&#xff0c;我是田洪川";obj.b"你是田洪川咋的&#xff0c;不得了啊&#xff1f;";obj.c"西西&#xff0c;哈哈&#xff0c;我是属性 c ";//上…

Matlab与数据结构 -- 对向量的排序

本图文介绍了Matlab怎样实现对向量的排序。

HashMap和HashSet原理及底层实现

HashMap底层用哈希算法实现&#xff0c;下面看一下哈希算表的整体概括&#xff1a; 当map.put(“key”,”values”);的时候&#xff0c;底层是这样的&#xff1a; static final Entry<?,?>[] EMPTY_TABLE {}; transient Entry<K,V>[] table (Entry<K,V&g…

Study on Android【三】--Intent消息传递

在前面写Android的ContentProvider时候&#xff0c;可以看到那是基于观察者模式的一个消息传递方法。每一个Cursor、ContentResolver做为一个小的注册中心&#xff0c;相关观察者可以在这个中心注册&#xff0c;更新消息由注册中心分发给各个观察者。而在MFC或Winform中&#x…

Matlab与数据结构 -- 对矩阵的排序

本图文介绍了Matlab怎样实现对矩阵的排序。

Java_中快速获取系统时间

直接调用System的currentTimeMillis()即可&#xff01; long start System.currentTimeMillis(); System.out.println("Start time : "start);

servlet必知细节(一)

servlet必知细节&#xff08;一&#xff09; 今天复习了一下servlet&#xff0c;有过一些编程经验后&#xff0c;与最初学习servlet相比&#xff0c;对servlet理解的角度不同了&#xff0c;最初只是学习了如何写一个servlet&#xff0c;api怎么用&#xff0c;现在从更深处了解了…

办公室“暧昧”的几种结局。

暧昧结局一&#xff1a;以消散结尾 外贸公司职员小雨 p: [( I J( n, i/ L( L 那是我刚来这家公司的时候&#xff0c;参加面试时我就关注到其中一个面试我的男人长得不错&#xff0c;言谈举止也都很儒雅&#xff0c;所以第一面就留下了深刻印象。等到我被录取正式上班后…

Matlab与机器学习-- 数据的归一化

本文介绍了Matlab对数据归一化的处理函数mapminmax。

ASP.NET中的母版页

ASP.NET中的母版页 添加一个"母版页",使用<asp:ContentPlaceHolder>挖坑,新建的母版页已经自动设置了两个ContentPlaceHolder创建使用母版页的具体页面,WebSite是新建"Web窗体"的时候勾选"选择模板页",WebApplication是新建"Web内容窗…

servlet必知细节(二)--servlet执行过程

servlet必知细节(二)--servlet执行过程 我们知道&#xff0c;servlet没有main函数&#xff0c;那么&#xff0c;servlet是怎么调用的呢&#xff1f;实际上&#xff0c;servlet 是由tomcat调用的&#xff0c;tomcat调用servlet程序执行。由调用栈可以看到&#xff0c;当一个请求…

workerman源码分析之启动过程

2019独角兽企业重金招聘Python工程师标准>>> http://www.cnblogs.com/CpNice/p/4714182.html 转载于:https://my.oschina.net/yonghan/blog/898076

MD5加密方法

/**//// <summary>/// 16位MD5加密方法/// </summary>/// <param name"str">原文</param>/// <returns>密文</returns>publicstaticstringgetMd5(stringstr){ MD5CryptoServiceProvider md5 new MD5CryptoServiceProvider()…

如何教计算机认识手写数字(上)

本图文介绍了一种简单的教会计算机识别手写数字的方法。