目录
ThreadLocal
线程本地存储,为变量在每个线程中都创建一个副本(在Thread中存储以ThreadLocal为key的值),每个线程可以访问自己内部的副本变量。
实现原理
ThreadLocal内部用ThreadLocalMap来存储变量值,实际上线程本地变量存储在Thread类的threadLocals ;变量中,做以下说明。
- get操作.首先获取当前线程,从当前线程中获取ThreadLocalMap对象,进而获取以ThreadLocal为key的value值并返回。
- 关于ThreadLocalMap,是在ThreadLocal对象中定义的,作为Thread类的属性,本质上是一个数组列表,且列表的元素[Entry]都是WeakReference[虚引用]的子类,意味着ThreadLocal中存储的成员在没有外部引用的情况下会被垃圾收集回收。
使用场景
并发场景下通过让不同线程持有相同资源的不同副本来避免线程安全问题,也可以用来提高系统并发性能。
与线程池相比,更容易封装,更容易从底层屏蔽掉多线程模型。同时不受控制的线程数量可能会带来资源耗尽。
常见的使用ThreadLocal来进行线程本地绑定一些非线程安全的对象(如网络客户端,读写客户端等等),并允许这些对象被垃圾收集机制回收,回收后允许进行再次绑定。
使用实例
FastDFS的StorageClient是非线程安全的。在进行大批量数据上传的时候单个StorageClient无法满足性能要求。
public class FDFSUtil {
private static Log log = LogFactory.getLog(FDFSUtil.class);
private static volatile ThreadLocal<StorageClient> threadLocal = new ThreadLocal<>();
private static final String DEFAULT_FILE_EXT_NAME = "jpg";
public String[] upload(byte[] image) {
try {
long start = System.currentTimeMillis();
String values[] = getClient().upload_file(image, DEFAULT_FILE_EXT_NAME, null);
long end = System.currentTimeMillis();
log.debug("UPLOAD FILE SUCCESS USE TIME:" + (end - start) + ",RESULT:" + Arrays.asList(values));
return values;
} catch (Exception e) {
log.error(e);
return null;
}
}
public String[] upload(byte[] image, String fileExtName) {
try {
long start = System.currentTimeMillis();
String values[] = getClient().upload_file(image, fileExtName, null);
long end = System.currentTimeMillis();
log.debug("UPLOAD FILE SUCCESS USE TIME:" + (end - start) + ",RESULT:" + Arrays.asList(values));
return values;
} catch (Exception e) {
log.error(e);
return null;
}
}
public byte[] download(String group, String path) {
try {
long start = System.currentTimeMillis();
byte[] image = getClient().download_file(group, path);
long end = System.currentTimeMillis();
log.debug("DOWNLOAD FILE SUCCESS USE TIME:" + (end - start));
return image;
} catch (Exception e) {
e.printStackTrace();
log.error(e);
}
return null;
}
public StorageClient getClient() {
if (threadLocal.get() == null) {
synchronized (threadLocal) {
if (threadLocal.get() == null) {
threadLocal.set(new StorageClient(null, null));
}
}
}
return threadLocal.get();
}
}