ThreadLocal:线程级别的私有变量
ThreadLocal使用:
1.set(T):将变量存放在线程中。
2.get(T):从线程中取得私有变量
3.remove():从线程中移除私有变量。
4.initialValue:初始化
5.withInitial:初始化
正常存取操作
initialValue+get
/**
* ThreadLocal正常存取操作
*/
package threadPool0523;
public class ThreadPoolDemo13 {
//创建并初始化
static ThreadLocal<String> threadLocal=
new ThreadLocal(){
@Override
protected Object initialValue() {
System.out.println("执行了初始化方法");
return "Java";
}
};
public static void main(String[] args) {
String result=threadLocal.get();
System.out.println("从ThreadLocal中得到 "+result);
}
}
initialValue + set + get会怎么执行
/**
* initialValue + set + get
* 怎么执行
*/
package threadPool0523;
public class ThreadPoolDemo14 {
static ThreadLocal<String> threadLocal=new ThreadLocal(){
@Override
protected Object initialValue() {
System.out.println("执行了初始化方法");
return "Java";
}
};
public static void main(String[] args) {
threadLocal.set("Mysql");
String result=threadLocal.get();
System.out.println("执行结果 "+result);
}
}
面试问题:
什么情况下不会执行initialValue? 为什么不会执行?
答:set之后就不会执行。ThreadLocal是懒加载的,当调用了get方法之后,才会尝试执行initialValue方法,尝试获取一下ThreadLocal set的值,如果获取到值,那么初始化方法不会被执行
withInitial方法(静态方法)
/**
*
* withinitial方法
*/
package threadPool0523;
import java.util.function.Supplier;
public class ThreadPoolDemo15 {
static ThreadLocal<String> threadLocal=
ThreadLocal.withInitial(new Supplier<String>() {
@Override
public String get() {
System.out.println("执行了初始化方法");
return "Java";
}
});
public static void main(String[] args) {
String result=threadLocal.get();
System.out.println("结果:"+result);
}
}
lamda表达式
public class ThreadPoolDemo16 {
static ThreadLocal<String> threadLocal=
ThreadLocal.withInitial(()->"Java");
public static void main(String[] args) {
String result=threadLocal.get();
System.out.println("结果:"+result);
}
}
ThreadLocal实现1000个时间格式化,无线程不安全问题
/**
* 实现1000个时间格式化
*/
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo17 {
static ThreadLocal<SimpleDateFormat> threadLocal=
ThreadLocal.withInitial(()->
new SimpleDateFormat("mm:ss"));
public static void main(String[] args) {
ThreadPoolExecutor executor=new ThreadPoolExecutor(10,10,0,
TimeUnit.SECONDS,new LinkedBlockingDeque<>(1000));
for (int i = 1; i <1001 ; i++) {
int finalI=i;
executor.execute(new Runnable() {
@Override
public void run() {
Date date=new Date(finalI*1000);
myFormat(date);
}
});
}
}
private static void myFormat(Date date) {
String result=threadLocal.get().format(date);
System.out.println("时间:"+result);
}
}
ThreadLocal使用场景:
1.解决线程安全问题
2.实现线程级别的数据传递
ThreadLocal 缺点:
1.不可继承(子线程中不能读取到父线程的值)
2.脏读(脏数据)
在一个线程中,读取到了不属于自己的数据
/**
* 使用线程池会出现 脏读
*/
package threadPool0523;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThradPoolDemo18 {
static ThreadLocal<String> threadLocal=
new ThreadLocal<>();
public static void main(String[] args) {
ThreadPoolExecutor executor=
new ThreadPoolExecutor(1,1,0,
TimeUnit.SECONDS,new LinkedBlockingDeque<>(1000));
for (int i = 0; i <2 ; i++) {
MyTask t=new MyTask();
executor.execute(t);
}
}
static class MyTask extends Thread{
//标识是否第一次访问
static boolean first = true;
@Override
public void run() {
if(first){
//第一次访问,存储用户信息
threadLocal.set(this.getName()+" session info.");
first=false;
}
String result=threadLocal.get();
System.out.println(this.getName()+": "+result);
}
}
}
线程使用ThreadLocal不会出现脏读->每个线程都使用自己的变量值和ThreadLocal
线程池里面使用ThreadLocal就会出现脏读数据,线程池会复用线程,复用线程之后,也会复用线程中的静态属性,从而导致某些方法不能被执行,于是就出现了脏数据的问题
脏数据的解决方案:
1.避免使用静态变量(静态属性会在线程池中复用)
2.使用remove解决
缺点3:内存溢出问题(最常出现问题)、打开数据连接但未关闭
内存溢出:当一个线程执行完之后,不会释放这个线程所占用内存,或者释放内存不及时的情况都叫做内存溢出。->线程不用了,但线程相关的内存还得不到及时的释放
内存溢出:ThreadLocal + 线程池(线程池是长生命周期)