ThreadLocal线程局部变量,是一种多线程间并发访问变量的解决方案。与其synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用以空间换时间的手段,为每个线程提供变量的独立副本,以保障线程安全。
从性能上说,ThreadLocal不具有绝对的优势,在并发不是很高的时候,加锁的性能会更好,但作为一套与锁完全无关的线程安全解决方案,在高并发量或者竞争激烈的场景,使用ThreadLocal可以在一定程度上减少锁竞争。
模拟:ThreadLocal的值是跟着线程走,下列代码中,共有三个线程,对同一个对象UseTreadLocal操作,主线程没有set所以为null,线程t1调用set方法存入“张三”,在t1线程中get值即为张三,线程t2调用set存入“李四”,那么t2线程get值为“李四”。
public class UseTreadLocal {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public String getThreadLocal() {
return threadLocal.get();
}
public void setThreadLocal(String value) {
threadLocal.set(value);;
}
public static void main(String[] args) {
UseTreadLocal utl = new UseTreadLocal();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
utl.setThreadLocal("张三");
System.out.println("当前t1:"+utl.getThreadLocal());
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
//Thread.sleep(1000);
utl.setThreadLocal("李四");
System.out.println("当前t2:"+utl.getThreadLocal());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"t2");
t1.start();
t2.start();
System.out.println("当前主线程:"+utl.getThreadLocal());
}
}
/*
* 模拟统一提交场景,当前线程执行完毕后再提交
*/
public class MessageHolder {
//创建容器对象,messages是一个集合,可以盛装消息
private List<String> messages = new ArrayList<>();
//创建ThreadLocal线程队列,并重写ThreadLocal的initialValue
public static final ThreadLocal<MessageHolder> holder = new ThreadLocal() {
/*
* 重写方法,并将MessageHolder在初始化的时候创建出来,
* 获取时就不会为null,即预加载机制
*/
@Override
protected Object initialValue() {
return new MessageHolder();
}
};
public static void add(String message) {
holder.get().messages.add(message);
}
public static List<String> clear(){
List<String> result = holder.get().messages;
holder.remove();
System.out.println("size:" + holder.get().messages.size());
return result;
}
public static void main(String[] args) {
MessageHolder.add("11111");
MessageHolder.add("22222");
MessageHolder.add("33333");
MessageHolder.add("44444");
MessageHolder.add("55555");
List<String> result = MessageHolder.clear();
System.out.println(result.toString());
}
}
知识点:
1.ThreadLocal应用场景,使用的是空间换时间的策略,为每个线程提供独立的副本进行数据存储;
2.从性能上说ThreadLocal不具有绝对的优势,在并发不是很高的时候,加synchronized锁的性能会更好,但是作为一套与锁完全无关的线程安全解决方案,在高并发量或者竞争激烈场景,使用ThreadLocal可以在一定程度上减少锁竞争。
例如场景:线程A执行m1方法后得到1,2然后要带着1,2执行m2方法,获得3,4,在带着数据执行m3方法,获得5,6,最后将1,2,3,4,5,6所有数据全部返还给线程A,并且执行的同时不影响其他线程执行方法。
/*传统实现方法必须加synchronized锁
* public synchronized allmethod(){
* return 1,2 = m1();
* return 3,4 = m2();
* return 5,6 = m3();
*
* all return 123456;
* }
*
*/
知识点:
ThreadLocal针对线程提供一个放置元素的副本,设置的数据只是针对单独的线程;