1.1 场景1
每个线程需要一个独享对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Random)
每个Thread内有自己的实例副本,不共享
比喻:教材只有一本,一起做笔记有线程安全问题。复印后没有问题,使用ThradLocal相当于复印了教材。
1.2 场景2
每个线程内需要保存全局变量(例如在拦截器中获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦
2.对以上场景的实践2.1 实践场景1
/**
* 两个线程打印日期
*/
public class ThreadLocalNormalUsage00 {
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage00().date(10);
System.out.println(date);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage00().date(104707);
System.out.println(date);
}
}).start();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT 开始计时
Date date = new Date(1000 * seconds);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return dateFormat.format(date);
}
}
运行结果
因为中国位于东八区,所以时间从1970年1月1日的8点开始计算的
/**
* 三十个线程打印日期
*/
public class ThreadLocalNormalUsage01 {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 30; i ) {
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage01().date(finalI);
System.out.println(date);
}
}).start();
//线程启动后,休眠100ms
Thread.sleep(100);
}
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT 开始计时
Date date = new Date(1000 * seconds);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return dateFormat.format(date);
}
}
运行结果
多个线程打印自己的时间(如果线程超级多就会产生性能问题),所以要使用线程池。
/**
* 1000个线程打印日期,用线程池来执行
*/
public class ThreadLocalNormalUsage02 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i ) {
int finalI = i;
//提交任务
threadPool.submit(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage02().date(finalI);
System.out.println(date);
}
});
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT 开始计时
Date date = new Date(1000 * seconds);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return dateFormat.format(date);
}
}
运行结果
但是使用线程池时就会发现每个线程都有一个自己的SimpleDateFormat对象,没有必要,所以将SimpleDateFormat声明为静态,保证只有一个
/**
* 1000个线程打印日期,用线程池来执行,出现线程安全问题
*/
public class ThreadLocalNormalUsage03 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
//只创建一次 SimpleDateFormat 对象,避免不必要的资源消耗
static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i ) {
int finalI = i;
//提交任务
threadPool.submit(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage03().date(finalI);
System.out.println(date);
}
});
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT 开始计时
Date date = new Date(1000 * seconds);
return dateFormat.format(date);
}
}
运行结果
出现了秒数相同的打印结果,这显然是不正确的。