本地变量副本
别人总是说用使用ThreadLocal
可以避免线程不安全的问题,因为ThreadLocal
使用的本地变量副本。怎么理解本地变量副本这个概念呢?
- 线程的创建和销毁是非常消耗资源的,所以有了线程池,重复利用线程
- 一个线程,每执行一个任务,任务中都去创建对象,例如
SimpleDateFormat
对象,同样很消耗资源 - 使用
ThreadLocal
给当前线程绑定一个SimpleDateFormat
对象,让这个线程每次执行任务,通过ThreadLocal#get
方法获取SimpleDateFormat
对象,代替每个任务执行中创建SimpleDateFormat
对象 - 注意:如果使用线程池做
SimpleDateFormat
测试,在任务中直接输出SimpleDateFormat
对象的地址,你会发现对象地址总是同一个,无论是正例还是反例。这是因为SimpleDateFormat
对象的hashCode
方法重写了,输出的是表达式的hashCode
。如果你要输出SimpleDateFormat
的真实的hashCode
可以使用System.identityHashCode(obj)
方法,在下面案例中有用到
总结:ThreadLocal
给当前线程绑定一个对象,当前线程在执行不同的任务时,通过ThreadLocal#get
获取当前线程绑定的对象。
正例:
public static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("mm:ss");
}
};
反例:
public static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
public static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return sdf;
}
};
SimpleDateFormat案例
正例:
public static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
public static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("mm:ss");
}
};
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
SimpleDateFormat simpleDateFormat = threadLocal.get();
String dateString = simpleDateFormat.format(new Date());
try {
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(dateString.equals(dateString2));
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(String.format("current thread %s fetch SimpleDateFormat identityHashCode %s SimpleDateFormat addr %s", Thread.currentThread().getName(), System.identityHashCode(simpleDateFormat), Integer.toHexString(System.identityHashCode(simpleDateFormat))));
}
});
}
fixedThreadPool.shutdown();
}
反例:
public static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
public static SimpleDateFormat staticSimpleDateFormat = new SimpleDateFormat("mm:ss");
public static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return staticSimpleDateFormat;
}
};
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
SimpleDateFormat simpleDateFormat = threadLocal.get();
String dateString = simpleDateFormat.format(new Date());
try {
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(dateString.equals(dateString2));
} catch (ParseException e) {
e.printStackTrace();
}
}
});
}
fixedThreadPool.shutdown();
}