在一个储物间, 里面并排摆放着许多储物柜, 一个人拿着自己的钥匙进入了储物间, 出来时手上拿着自己的行李, 别人也拿着自己的钥匙进了储物间, 但是虽然进的是同一个储物间, 打开的当然是另一个储物柜, 使用者都会从各自的储物柜中取出自己的行李
Specific 就是特定的意思, Storage 是存储柜, 因此, 所谓 Thread-Specific Storage 就是 "每一个线程特有的存储柜, "为每个线程准备存储空间的意思", 这是一种即使只有一个入口, 也会在内部为每个线程分配特有的存储空间的模式
二. 示例程序(当不使用该模式时)| 类名 | 说明 |
| Log | 创建日志的类 |
| Main | 测试程序行为的类 |
Main 类会使用 Log 类慢慢的打印日志
public class Main {
public static void main(String[] args) {
System.out.println(" begin ");
for (int i = 0; i < 10; i++) {
Log.println(" main : i = " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
//
}
Log.close();
System.out.println(" end ");
}
}
}
2.2 Log类
Log 类用于在名为 log.txt 的文件中记录程序运行记录的类
public class Log {
private static PrintWriter writer = null;
static {
try {
writer = new PrintWriter(new FileWriter("log.txt"));
} catch (IOException e) {
//
}
}
public static void println(String s) {
writer.println(s);
}
public static void close() {
writer.println(" ==== end of log ==== ");
writer.close();
}
}
2.3 运行示例
main : i = 0 main : i = 1 main : i = 2 main : i = 3 main : i = 4 main : i = 5 main : i = 6 main : i = 7 main : i = 8 main : i = 9 ==== end of log ====三. 示例程序(当使用该模式时)
上面的是在普通不过的类, 调用 println 方法, 程序会将字符串写入日志文件中, 仅此而已, 下面的程序会使用不同的线程将字符串写入不同的日志文件中, 不过, 写入字符串与上面的程序完全相同, 都必须调用 println 方法, 其实, 我们要制作一种结构, 在其中编写一个继承了 Thread 的类, 但是并不在它的字段中保存 PrintWrite 实例, 而是与上面的实例一样, 只是机械的调用 Log.println() 方法, 仅仅这样就实现自动的将字符串分配到各个线程的日志文件中
TerminationRequester ⻆⾊负责向 Temninator ⻆⾊发出终⽌清求, 在示例程序中, 由 Main 类扮演此⻆⾊
实例程序一览表
| 类名 | 说明 |
| Log | 创建日志的类 |
| Main | 测试程序行为的类 |
| TSLog | 创建日志的类(实例为各个线程所有) |
| java.lang.ThreadLocal | 分配线程特有的存储空间的类 |
| ClientThread | 表示调用Log的线程的类 |
由于现在需要为每个线程创建一个 TSLog 实例, 所以需要将静态字段和静态方法修改为实例字段和实例方法, 同时需要将 statis 代码块的初始化移动到构造函数中
public class TSLog {
private PrintWriter writer = null;
public TSLog(String fileName) {
try {
this.writer = new PrintWriter(new FileWriter(fileName));
} catch (IOException e) {
//
}
}
public void println(String s) {
writer.println(s);
}
public void close() {
writer.println(" ==== end of log ==== ");
writer.close();
}
}
3.2 Log类
和上面的 Log 类, 他们对外公开的声明是完全一致的, 但是他们的实现却完全不同
public class Log {
private static final ThreadLocal tsLogThreadLocal = new ThreadLocal<>();
public static void println(String s) {
getTsLog().println(s);
}
public static void close() {
getTsLog().println(" ==== end of log ==== ");
getTsLog().close();
}
private static TSLog getTsLog() {
TSLog tsLog = tsLogThreadLocal.get();
// 第一次调用要初始化一个
if (tsLog == null) {
tsLog = new TSLog(Thread.currentThread().getName() + "-log.txt");
tsLogThreadLocal.set(tsLog);
}
return tsLog;
}
}
3.3 ClientThread类
这里就是开启多线程去调用 priintln 方法
public class ClientThread extends Thread {
public ClientThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println(" begin ");
for (int i = 0; i < 10; i++) {
cn.demo10.Log.println(" main : i = " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
//
}
}
Log.close();
System.out.println(" end ");
}
}
3.4 Main类
public class Main {
public static void main(String[] args) {
new ClientThread("alice").start();
new ClientThread("bobby").start();
new ClientThread("chris").start();
}
}
3.5 运行结果
可以看出, 虽然3个线程调用的是 Log 的同一个方法, 但是实际上他们使用的却是各个线程特有的 TSLog 的实例
bobby begin chris begin alice begin end end end3.6 使用了该模式的Timethreads图 四. Thread-Specific Storage模式登场的角色 4.1 Client(委托者)
Client 角色将处理委托给 TSObjectProxy, 一个 TSObjectProxy 会被多个 Client 角色使用, 在实例程序中, 有 ClientThread 类扮演
4.2 TSObjectProxy(线程特有的对象的代理人)TSObjectProxy 会执行多个 Client 角色委托给它的处理, 在示例程序中, 有 Log 扮演此角色
4.3 TSLogThreadLocal(线程特有的对象集合)当 getTsLog() 方法被调用后, 回去查询map, 返回与 Client 角色对应的 TsObject, 另外, 当 setTsLog() 方法被调用后, 他还会将 Client 角色与 TsObject 角色之间的对应关系维护到 ThreadLocal 的 map 中, 在示例程序中, 有 ThreadLocal 扮演
4.4 TSObject(线程特有的对象)TSObject 保存者线程特有的信息, 由 ThreadLocal 管理, TSObject 角色中的方法只会被单线程调用, 在示例程序中, 由 TSLog 类扮演此角色
4.5 模式类图



