ThreadLocal
ThreadLocal的用途
场景1:每个线程都需要一个独享的对象
public class ThreadLocalNormalUsage00 {
public static void main(String[] args) {
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(1007);
System.out.println(date);
}
}).start();
}
public String date(int seconds){
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return simpleDateFormat.format(date);
}
}
引出问题
当所有对象都共用一个simpledateformat对象的时候,就会发生线程安全问题。
使用静态对象在多线程环境下是不行的。
/**
* @Classname ThreadLocalNormalUsage00
* @Description 1000个打印日期的任务,用线程池执行;此时线程共享一个静态对象会发生线程安全问题。
* @Date 2021/2/17 9:24
* @Created by YoungLiu
*/
public class ThreadLocalNormalUsage03 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage03().date(finalI);
System.out.println(Thread.currentThread().getName() + date);
}
};
threadPool.submit(runnable);
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
return simpleDateFormat.format(date);
}
}
使用synchronized关键字也可以解决多线程下共享静态对象的线程安全问题
/**
* @Classname ThreadLocalNormalUsage00
* @Description 加锁来解决线程安全问题
* @Date 2021/2/17 9:24
* @Created by YoungLiu
*/
public class ThreadLocalNormalUsage04 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage04().date(finalI);
System.out.println(Thread.currentThread().getName() + date);
}
};
threadPool.submit(runnable);
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
String format;
synchronized (ThreadLocalNormalUsage04.class){
format = simpleDateFormat.format(date);
}
return format;
}
}
使用ThreadLocal工具类来解决此类共享对象的任务
/**
* @Classname ThreadLocalNormalUsage00
* @Description 利用ThreadLocal,给每个线程分配自己的dateFormat对象,保证了线程安全,高效利用内存。
* @Date 2021/2/17 9:24
* @Created by YoungLiu
*/
public class ThreadLocalNormalUsage05 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage05().date(10000 + finalI);
System.out.println(Thread.currentThread().getName() + date);
}
};
threadPool.submit(runnable);
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
//这个里的get方法返回的是 initialValue()的返回对象
SimpleDateFormat simpleDateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
return simpleDateFormat.format(date);
}
}
class ThreadSafeFormatter {
public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal =
new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
}
};
}
场景2:线程内方法共享信息问题
解决思路
/**
* @Classname ThreadLocalNormalUsage06
* @Description 演示ThreadLocal用法2:避免传递参数的麻烦
* @Date 2021/2/17 10:19
* @Created by YoungLiu
*/
public class ThreadLocalNormalUsage06 {
public static void main(String[] args) {
Service1 service1 = new Service1();
service1.process();
}
}
class Service1{
public void process(){
User user = new User("超哥");
UserContextHolder.holder.set(user);
new Service2().process();
}
}
class Service2{
public void process(){
User user = UserContextHolder.holder.get();
System.out.println("Service2拿到用户名"+user.name);
new Service3().process();
}
}
class Service3{
public void process(){
User user = UserContextHolder.holder.get();
System.out.println("Service3拿到用户名:"+user.name);
}
}
class UserContextHolder{
public static ThreadLocal<User> holder = new ThreadLocal<>();
}
class User{
String name;
public User(String name){
this.name=name;}
}
ThreadLocal的两个作用
ThreadLocal详解
源码分析
每一个Thread都有一个ThreadLocalMap,这个map里面存储了很多ThreadLocal
ThreadLocal主要方法介绍