Thread-Specific Storage Pattern
来源:互联网 发布:量子引力 知乎 编辑:程序博客网 时间:2024/06/05 04:51
什么是Thread-Specific Storage Pattern?
现在有一个保管箱,许多投币保管箱都排在一起,每个人拿着自己的要是进来了,退出的时候拿着自己的行李。又有一个人拿着自己的钥匙进来了,打开自己的保管箱,拿走自己的行李,而不会破坏其他人的行李。
这个模式名称翻译成中文就是“线程独有的储藏库”。这个模式只有一个入口,但是内部对每个线程提供特有的存储空间。
首先介绍以下java.lang.ThreadLocal类
java.lang.ThreadLocal的实例可以想象成一种集合架构(collection)。也就是说,ThreadLocal的实例只有一个,但是可以管理多个对象。因为ThreadLocal的实例管理了多个对象,所以ThreadLocal拥有存放(set)和取得(get)方法。
public void set()
ThreadLocal类的实例有一个set方法,可以将参数指定的实例存放到调用set方法的线程所对应的存储空间。这里存放的实例,可以调用get方法取得。这个set方法是没有用传入参数的,而是检查当前线程,也就是Thread.currentThread()的值,自动以这个值作为key存放实例,相当于把自己的行李放进保管箱一样。
public Object get()
ThreadLocal类的get方法,可以调用get方法的线程对应的实例(现在线程对应的实例)。之前set的实例,就是现在get的返回值。如果没有set过,就返回null。调用get时,相当于从自己的保管箱拿出行李一样。与set方法一样,get方法没有表示线程的参数,因为程序会在get里自己去检查现在的线程,也就是自己本身就是键值。
下面设想一种情况,我们设置3个线程,每个线程负责对一个不同的文件进行写入。利用ThreadLocal,我们给每个线程设置独立的文件名称属性,使得线程得以对不同文件进行写入。
下面我们的代码测试
首先是我们的TSLog(表示的意思是ThreadSpecificLog,线程特有Log类)类:
package justTest;import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter;public class TSLog { private PrintWriter writer = null; // 初始化writer字段 public TSLog(String fileName) { try { writer = new PrintWriter(new FileWriter(fileName)); } catch (IOException e) { e.printStackTrace(); } } // 加入一条log public void println(String s) { writer.println(s); } // 关闭log public void close() { writer.println("====End of log===="); writer.close(); }}接下来是我们的Log类,主要作用是获取当前线程的TSLog,然后调用其方法:
package justTest;public class Log { private static final ThreadLocal<TSLog> tsLogCollection = new ThreadLocal<TSLog>(); //加入一条log public static void println(String s){ getTSLog().println(s); } //关闭log public static void close(){ getTSLog().close(); } //取得线程特有的log private static TSLog getTSLog(){ TSLog tsLog = (TSLog)tsLogCollection.get(); //如果线程第一次调用 就建立新文件并且注册log if(tsLog == null){ tsLog = new TSLog(Thread.currentThread().getName()+"-log.txt"); tsLogCollection.set(tsLog); } return tsLog; }}下面是ClientThread类,继承自Thread类,使用Log.println和Log.close方法。
package justTest;public class ClientThread extends Thread { public ClientThread(String name){ super(name); } public void run(){ System.out.println(getName()+"BEGIN"); for(int i = 0;i<10;i++){ Log.println("i="+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } Log.close(); System.out.println(getName()+"END"); }}最后是我们的Test类,启动ClientThread线程:
package justTest;public class Test { public static void main(String[] args) { new ClientThread("Alice").start(); new ClientThread("Bobby").start(); new ClientThread("Chris").start(); }}现在回头再来看看我们的代码:
在TSLog类中,这个类负责建立线程特有的log记录,而Log类负责调用TSLog类下的方法以及获得线程特有的log,骑宠tsLogCollection字段就是用来储藏每个线程的TSLog实例的保管箱。其他的没什么可说的,前面几篇文章说太多了。
Thread-Specific Storage Pattern的所有参与者
Client(委托人)参与者
Client参与者将工作委托给TSObjectProxy参与者。一个TSObjectProxy参与者可由多个Client参与者一起使用。比如示例中的ClientThread类。
TSObjectProxy(线程独有对象的代理者)参与者
TSObjectProxy参与者会处理多个Client参与者委托的工作。
首先,TSObjectProxy参与者会使用TSObjectCollection参与者,取得Client参与者所对应的TSObject参与者。并且将工作委托给TSObject参与者,比如示例中的Log类。
TSObjectCollection(线程独有对象的集合)参与者
TSObjectCollection参与者拥有Client参与者与TSObject参与者的对照表。
当getTSObject被调用时,就检查对照表,返回Client参与者对应的TSObject对象。而setTSObject方法被调用时,则在对照表里设置Client参与者与TSObject参与者的组合。比如示例中的ThreadLocal类。
TSObject(线程独有的对象)参与者
TSObject参与者存放有线程特有的信息,这个参与者由TSObjectCollection管理。TSObject参与者的方法只会由单线程调用。比如示例中的TSLog类。
扩展思考
局部变量与java.lang.ThreadLocal类
线程本来就有特有的区域,就是存放方法局部变量的堆栈。在方法里分配的局部变量都是线程独有的,无法被其他线程访问到。但是这些变量一旦推出方法就会消失。而ThreadLocal则是和方法调用无关,为线程分配特有空间的类。
放置线程特有信息的地方
1、线程外(thread-external)
java.lang.ThreadLocal的示例可以说是保管箱间。每个线程的保管箱都集中在保管箱间里。线程不会带着保管箱到处跑。像这样就被称为线程外的信息存放。这样的方式不需要修改表示线程的现有类,然而也存在线程类不容易被阅读的危险,毕竟无法得知其他类中存放有线程的信息。
2、线程内(thread-internal)
现在有一个Thread类的子类MyThread,MyThread的字段就是线程特有的信息,这时我们就称呼位线程内存放的信息。
将特有信息存放在线程外就像是自己所有,但是自己不一定带在身上;
而特有信息存放在线程的方式,就像拿在自己手上一样。
不必担心被其他线程访问
这个模式下线程独有的内存空间是不用担心被其他线程擅自乱动的。
这是很重要的性质,共享互斥是很重要的,但是实现共享互斥并不容易,而且要求共享实例不损坏,共享互斥执行性能偏低。然而这个模式保证了线程内部的属性绝对不会被其他线程碰到,而且没有出现进行共享互斥的操作(也许底层有,但是至少不用我们写),是很方便的架构。毕竟这个模式没有出现SharedResource。
throughput的提升取决于实现
虽然这个模式需要我们写的代码没有共享互斥,但是程序的throughput不见得一定比Single Threaded Execution Pattern来得高。因为互斥控制可能存在于TSObjectCollection参与者内,另外通过TSObjectProxy参与者调用方法也需要花费时间(毕竟要通过TSObject)。
然而这个模式的优点不是在于throughput上,而是复用性:
1、不需要改变程序的结构
2、互斥共享不在表面上,减少了犯错的机会
隐藏了context
这应该可以说是这个模式的一个缺点了,TSObjectCollection参与者(相当于ThreadLocal)会自己判断现在的线程而不需要程序员传入,虽然方便,但是也危险。
- Thread-Specific Storage Pattern
- Thread-Specific Storage
- Thread-Specific Storage
- Thread-Specific Storage 模式
- Thread Specific Storage
- 第十一章 Thread-Specific Storage
- 利用Thread-Specific Storage撰寫一個HibernateUtil
- 利用Thread-Specific Storage撰寫一個HibernateUtil
- 线程特有存储模式(Thread Specific Storage)
- 线程局部存储 Thread Local Storage 线程特定数据 Thread-Specific Data
- 多线程设计模式——Thread Specific Storage(线程特有存储)模式
- Thread-specific data
- Thread-specific data
- 线程私有数据thread-specific
- python thread wait until specific thread complete
- Thread-Local Storage
- TLS (Thread local storage)
- Thread-Local Storage(TLS)
- css3-边框圆角阴影渐变
- 第六章 内部类和异常类 编程题
- HDU
- 石子合并_直线型 动态规划
- 思考
- Thread-Specific Storage Pattern
- mysql数据库数据类型之整型
- 二叉树前中后序遍历
- 数据结构实验之图论九:最小生成树
- JAVA学习日志(1)
- 习题6(6.14)
- mac+nginx+redis+php+composer的安装和配置
- 2017年12月1日训练笔记
- 根据韦东山视频写最小根文件系统