一,简述
除了控制资源的访问外,还可通过增加资源来保证线程安全。ThreadLocal主要解决为每个线程绑定自己的值。
二,基本使用
实例一
/**
* ThreadLocal的基本使用
* Author: DRHJ
* Date: 2022/7/19 21:40
*/
public class Test01 {
//定义ThreadLocal对象
static ThreadLocal threadLocal = new ThreadLocal();
public static void main(String[] args) {
SubThread s1 = new SubThread();
SubThread s2 = new SubThread();
s1.start();
s2.start();
}
//定义线程类
static class SubThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
//设置线程关联的值
threadLocal.set(Thread.currentThread().getName() + " - " + i);
//调用get()方法读取关联的值
System.out.println(Thread.currentThread().getName() + " value = " + threadLocal.get());
}
}
}
}
每个线程都有自己的值,互不干涉。
实例二
/**
* 在多线程环境中,把字符串转换为日期对象,多个线程使用同一个SimpleDateFormat对象可能会产生线程安全问题,有异常
* 为每个线程指定自己的SimpleDateFormat对象,使用ThreadLocal
* Author: DRHJ
* Date: 2022/7/19 21:46
*/
public class Test02 {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new ParseDate(i)).start();
}
}
static class ParseDate implements Runnable {
private int i = 0;
public ParseDate(int i) {
this.i = i;
}
@Override
public void run() {
String text = "2022-07-19 21:53:" + i % 60;
try {
Date date = sdf.parse(text);
System.out.println(i + " -- " + date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
抢这使用同一个SimpleDateFormat对象,导致出现了问题
/**
* 在多线程环境中,把字符串转换为日期对象,多个线程使用同一个SimpleDateFormat对象可能会产生线程安全问题,有异常
* 为每个线程指定自己的SimpleDateFormat对象,使用ThreadLocal
* Author: DRHJ
* Date: 2022/7/19 21:46
*/
public class Test02 {
static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new ParseDate(i)).start();
}
}
static class ParseDate implements Runnable {
private int i = 0;
public ParseDate(int i) {
this.i = i;
}
@Override
public void run() {
String text = "2022-07-19 21:53:" + i % 60;
try {
//判断当前对象是否有SimpleDateFormat对象,如果当前线程没有就创建一个,如果有就直接使用
if (threadLocal.get() == null) {
threadLocal.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
Date date = threadLocal.get().parse(text);
System.out.println(i + " -- " + date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
成功执行
三,ThreadLocal初始值
ThreadLocal初始值是null,可以通过继承ThreadLocal重写initialValue()方法设置初始值
/**
* ThreadLocal初始值,定义ThreadLocal类的子类,在子类中重写initialValue()方法指定初始值,再第一次调用get()方法不会返回null
* Author: DRHJ
* Date: 2022/7/20 20:56
*/
public class Test03 {
//定义ThreadLocal的子类
static class SubThreadLocal extends ThreadLocal<Date> {
//重写
@Override
protected Date initialValue() {
return new Date(System.currentTimeMillis() - 1000 * 60 * 15);
}
}
//定义ThreadLocal对象
static SubThreadLocal threadLocal = new SubThreadLocal();
//定义线程类
public static void main(String[] args) {
SubThread t1 = new SubThread();
t1.start();
SubThread t2 = new SubThread();
t2.start();
}
//定义线程类
static class SubThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//第一次调用threadLocal的get()方法会返回null
System.out.println("----------" + Thread.currentThread().getName() + " value= " + threadLocal.get());
//如果没有初始值就设置当前日期
if (threadLocal.get() == null) {
System.out.println("*************");
threadLocal.set(new Date());
}
try {
Thread.sleep(new Random().nextInt(500));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}