一.解决线程安全问题
1.线程安全的解决方案:
加锁(synchronized,Lock),使用私有变量
2.加锁可以解决线程安全的问题,但因为要同时进行排队处理,所以会带来一定的性能消耗
3.有没有一种方案既可以避免加锁排队执行,又不会每次执行任务都需要重新创建私有变量?
使用ThreadLocal线程的本地变量,每个线程创建一个私有变量(以1000个任务10个线程池的示例来说,使用ThreadLocal就是创建10个私有变量
选择ThreadLocal还是锁?
就看创建实例对象之后的复用率,复用率高就是ThreadLocal
二.ThreadLocal的使用(ThreadLocal线程级别的私有变量)
5种方法:
1.set(T):将私有变量设置到线程中(每个线程有自己的变量)
2.get():获取线程中的私有变量
3.remove():从线程中移除私有变量的
public class ThreadLocalDemo1 {
//创建ThreadLocal
private static ThreadLocal<String> threadLocal=new ThreadLocal<>();
public static void main(String[] args) {
//定义公共任务
Runnable runnable=new Runnable() {
@Override
public void run() {
//得到线程名称
String tname=Thread.currentThread().getName();
System.out.println(tname+"设置值:"+tname);
try {
//set ThreadLocal
threadLocal.set(tname);
//执行ThreadLocal打印
printThreadLocal();
}finally {
//移除ThreadLocal
threadLocal.remove();
}
}
};
Thread t1=new Thread(runnable,"线程1");
t1.start();
Thread t2=new Thread(runnable,"线程2");
t2.start();
}
private static void printThreadLocal() {
//从ThreadLocal中获取值
String result=threadLocal.get();
System.out.println(Thread.currentThread().getName()+"中取值:"+result);
}
}
4.initialValue 创建ThreadLocal时设置默认值
注意事项:类型要保持一致
public class ThreadLocalDemo2 {
//创建和初始化ThreadLocal
private static ThreadLocal<SimpleDateFormat> threadLocal=
new ThreadLocal(){
@Override
protected SimpleDateFormat initialValue() {
System.out.println("执行initialValue");
return new SimpleDateFormat("mm:ss");
}
};
public static void main(String[] args) {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
Date date=new Date(1000);
//从ThreadLocal获取SimpleDateFormat对象,并格式化时间
String result=threadLocal.get().format(date);
System.out.println("线程1时间格式化"+result);
}
});
t1.start();
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
Date date=new Date(2000);
//从ThreadLocal获取SimpleDateFormat对象,并格式化时间
String result=threadLocal.get().format(date);
System.out.println("线程2时间格式化"+result);
}
});
t2.start();
}
}
public class ThreadLocalDemo3 {
//创建并初始化ThreadLocal
private static ThreadLocal<Integer> threadLocal=
new ThreadLocal(){
@Override
protected Object initialValue() {
int num=new Random().nextInt(10);
System.out.println("执行了initialValue生成了:"+num);
return num;
}
};
public static void main(String[] args) {
//创建线程池
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000));
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
//get ThreadLocal
int result=threadLocal.get();
System.out.println(Thread.currentThread().getName()+"得到的结果1:"+result);
}
});
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
//get ThreadLocal
int result=threadLocal.get();
System.out.println(Thread.currentThread().getName()+"得到的结果2:"+result);
}
});
}
}
5.withInitial 创建ThreadLocal时设置默认值
public class ThreadLocalDemo4 {
//创建并初始化ThreadLocal
private static ThreadLocal<String> threadLocal=
ThreadLocal.withInitial(new Supplier<String>() {
@Override
public String get() {
System.out.println("执行了withInitial方法");
return Thread.currentThread().getName()+"java";
}
});
public static void main(String[] args) {
Runnable runnable=new Runnable() {
@Override
public void run() {
//获取ThreadLocal
String result=threadLocal.get();
System.out.println(Thread.currentThread().getName()+" 获取到的内容:"+result);
}
};
Thread t1=new Thread(runnable,"线程1");
t1.start();
Thread t2=new Thread(runnable,"线程2");
t2.start();
}
}
创建并初始化的简单版本(JDK8+的lamda表达式):
public class ThreadLocalDemo5 {
//简单写法
private static ThreadLocal<String> threadLocal=
ThreadLocal.withInitial(()->"java");
public static void main(String[] args) {
//创建线程池
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000));
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
//get ThreadLocal
String result=threadLocal.get();
System.out.println(Thread.currentThread().getName()+"得到的结果1:"+result);
}
});
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
//get ThreadLocal
String result=threadLocal.get();
System.out.println(Thread.currentThread().getName()+"得到的结果2:"+result);
}
});
}
}