线程封闭技术
1:线程封闭技术概述
如果数据和对象被封闭在一个线程中,就不会产生需要同步的问题,因为不会有其他线程访问这些数据和对象。这也是最简单的实现线程安全的方法之一。就在在Java语言中并没有强制规定变量必须由锁保护,也同样没有提供强制将对象封闭在某个线程中的方式。如何实现线程封闭是程序设计要考虑的,在Java语言有一些机制可以实现线程封闭,如局部变量很显然是线程封闭的,以及ThreadLocal类,即便如此在程序设计中,依然要考虑如何确保封闭在线程的对象不会再线程中逸出。
谈到逸出一般有以下几种情况会出现逸出:
- 将对象引用保存在公有的静态的public static 引用变量中,这种方式任何类和所有线程都能访问该对象。
- 发布集合类的时候,类中存放的对象也会一道逸出来。
- 发布内部类的时候,用包含外部类的引用,这这样子就会外部类也会逸出啦。
2:常用的线程封闭技术
2.1:Ad-hoc线程封闭
该技术是指,维护线程封闭的职责完全由程序实现来承担,这种技术十分脆弱。
2.2:栈封闭
栈封闭就是指通过局部变量指向heap中对象的方式,因为局部变量处于JVM stack中,而该stack是线程独有的,所以该线程独有的变量指向的对象也是线程封闭,只属于该线程。这里要再讲解以下JVM中内存区域介绍。
2.2:ThreadLocal类
认真仔细学习以下ThreadLocal类的使用方法。
2.2.1:ThreadLocal类的使用方法
ThreadLocal是Thread内部用来存放数据的ThreadLocalMap的管理者,因为ThreadLocalMap是ThreadLocal工具类的内部静态的类,所以该类不依赖于ThreadLocal存在而存在。Thread内部有两个ThreadLocalMap引用初始化为null。这个工具类的意义就是在通过ThreadLoca类来向每个Thread内部的Map中写入以ThreadLocal实例对象的key存储value。
示例代码;
import java.util.ArrayList; import java.util.List; public class Main { private static ThreadLocal<List<String>> threadLocal = new ThreadLocal<>(); public void setThreadLocal(List<String> value) { threadLocal.set(value); } public void getThreadLocal() { List<String> l = threadLocal.get(); for(String s:l){ System.out.println(Thread.currentThread().getName()+" "+s); } } public static void main(String[] args){ //通过,示例一个包含ThreadLocal的对象, //来当问不同thread的map final Main test = new Main(); new Thread(new Runnable() { @Override public void run() { List<String> strs = new ArrayList<String>(); strs.add("1"); strs.add("2"); strs.add("3"); test.setThreadLocal(strs); test.getThreadLocal(); } },"t1").start(); new Thread(new Runnable() { @Override public void run() { List<String> strs = new ArrayList<String>(); strs.add("a"); strs.add("b"); strs.add("c"); test.setThreadLocal(strs); test.getThreadLocal(); } },"t2").start(); } }
结果:
用法如上,通过一个ThreadLocal对象实例,来访问不同Thread类的内部map,将数据存储在线程私有的Map里!
2.2.2:ThreadLocal类源码解读
ThreadLocal类中,主要有三个使用方法,get(),set()和remove()方法。
- T get()
public T get() {
//获取使用该方法的线程 Thread t = Thread.currentThread();
//然后再通过该线程获取其内部的ThreadLocalMap ThreadLocalMap map = getMap(t);
//如果map不为null, if (map != null) {
//则通过this对象,即当前ThreadLocal实例来获得对应的Entry。 ThreadLocalMap.Entry e = map.getEntry(this);
//如果没有获得null,则用当前value,创建一个result,返回。
if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
-
void set(T value)
public void set(T value) {
//获得当前线程 Thread t = Thread.currentThread();
//获得当前线程的ThreadLocalMap ThreadLocalMap map = getMap(t);
//如果map不为null if (map != null)
//则通过ThreadLocalMap的set方法以this为key,写入value。
//this就是TheadLocal的实例
map.set(this, value); else
//否则,为当前线程创建一个新的ThreadLocalMap。 createMap(t, value); }
- void remove()
public void remove() {
//获取当先线程的map ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null)
//不为null,则移除以当前ThreadLocal的key,value的键值对。
m.remove(this); }
2.2.3:ThreadLocal类避免内存泄露