什么是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();}}// 加入一条logpublic void println(String s) {writer.println(s);}// 关闭logpublic 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>();//加入一条logpublic static void println(String s){getTSLog().println(s);}//关闭logpublic static void close(){getTSLog().close();}//取得线程特有的logprivate static TSLog getTSLog(){TSLog tsLog = (TSLog)tsLogCollection.get();//如果线程第一次调用 就建立新文件并且注册logif(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相关推荐

  1. Thread Specific Storage

    这个模式是这样的:所有线程都有一个入口,但是内部为每个线程分配了特有的存储空间. 这里需要用到ThreadLocal类,这个类的实例可以想象成保管箱间,他有大量的保管箱房间.ThreadLocal的实 ...

  2. TLS(Thread Local Storage)问题demo

      C++11中的thread_local是C++存储期的一种,属于线程存储期.存储期定义C++程序中变量/函数的范围(可见性)和生命周期.C++程序中可用的存储期包括auto.register.st ...

  3. thread local storage

    有时会需要这种模式,一个全局变量,需要在程序的任何地方都可以使用它,但是当这个变量出现在不同线程时,就要求系统将这个变量拷贝到各个线程中,这样的话,每个线程内部也可以随时访问本线程的全局变量,但是线程 ...

  4. TSD(Thread Specific Data)线程专有数据

    (1)全局变量 (2)局部变量 (3)TSD(Thread-Specific Data 线程专有数据) 1.http://upczap.itpub.net/ 在单线程的程序里,有两种基本的数据:全局变 ...

  5. java线程本地存储_[并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)-win32和pthread比较]...

    场景: 1.  需要统计某个线程的对象上创建的个数. 2. 当创建的堆空间需要根据线程需要创建和结束时销毁时. 3. 因为范围是线程只能看到自己的存储数据,所以不需要临界区或互斥量来维护自己的堆内存. ...

  6. Thread Per Message Pattern

    概述:对每个命令或请求分配一个线程,由这个线程执行工作. 不像是一个有实权的将军,而像是一个传递消息的太监. when: 原理进一步解析: 利用了调用方法与启动线程两个操作,实现"传送异步消 ...

  7. IoC容器和 Dependency Injection模式 Inversion of Control Containers and the Dependency Injection pattern

    原文链接:http://www.martinfowler.com/articles/injection.html 文末有中文翻译版本,martinfowler上有提供中文版的pdf,也可以自行下载 中 ...

  8. IoC Containers and the DI pattern

    2019独角兽企业重金招聘Python工程师标准>>> In the Java community there's been a rush of lightweight contai ...

  9. java 线程局部存储,转载boost::thread简要分析(3):线程局部存储及其它

    多线程编程中还有一个重要的概念:Thread Local Store(TLS,线程局部存储),在boost中,TLS也被称作TSS,Thread Specific Storage. boost::th ...

  10. JAVA多线程设计模式篇 12、Thread-Specific Storage模式——给我个柜子

    文章目录 1. ThreadLocal的使用示例 2. ThreadLocal的使用场景 2.1 线程隔离的数据库连接与事务 2.2 线程隔离的session会话 总结 多线程环境中即然共用资源这么困 ...

最新文章

  1. L1-023 输出GPLT (C++解决,含题解)
  2. python 播放视频 ftp_利用 Python 实现文件自动备份到 ftp 服务器
  3. 取消Exchange数据库的自动分配功能
  4. PowerEdge R730安装Windows server2008 R2操作系统
  5. python dataframe取一列_python - 从pandas DataFrame列标题中获取列表
  6. UVa 1354 天平难题 枚举二叉树
  7. 小肚皮最新版本_小肚皮官方版下载_小肚皮app - Win7旗舰版
  8. Docker开启和关闭容器自启动
  9. matplotlb 进阶之Styling with cycler
  10. c++ idea 插件_IDEA的基本使用:让你的IDEA有飞一般的感觉
  11. 加速与缓存技术之Varnish
  12. CentOS8 安装 telnet 命令
  13. 一起欣赏:50+ 极具创意的个人简历设计【下篇】
  14. 死锁——哲学家就餐问题
  15. 需求分析说明书、概要设计说明书、详细设计说明书部分样例
  16. mac桌面键盘快捷键_使用键盘快捷键更改桌面分辨率
  17. pyhon3爬取百度搜索结果
  18. 初学 博弈论 又称对策论 Game Theory
  19. 买极米NEW Z8X的我后悔了,换成当贝F3 Air可真香
  20. OPPO find5(X909)Omni刷机包 原生安卓4.4.2 超流畅省电

热门文章

  1. 重组人骨形态发生蛋白-2(BMP 2)的研究意义
  2. Vue 点击按钮,下载视频
  3. 【JAVA基础速过】第3章 数组+Arrays工具类的使用
  4. PAT-2021年春季乙级考试题解
  5. Android视频直播的实现
  6. [应用抓包] r0capture应用层通用抓包使用文档
  7. html脚注如何设置,word2010脚注文本怎么设置
  8. 我们不应该歧视任何的编程语言,因为他们都是萌娘
  9. cs285深度强化学习课程笔记-lec1
  10. 暴力破解防范措施和措施总结