一、为什么要使用
我们在先前的文章《synchronized关键字使用详解》中曾经介绍过,什么场景下不需要考虑线程安全的问题,其中一种场景就是“变量为线程独享变量的时候”,当时的例子如下:
@Slf4j
public class MyThread implements Runnable {
// 线程独享的变量
private int count = 200;
@Override
public void run() {
count--;
log.info("当前线程名称:{},计数器为:{}", Thread.currentThread().getName(), count);
}
}
在这个例子中,我们的变量是在线程类中的,它并没有使用额外的特殊的修饰符,每启动一个线程,则每个线程都会有一个自己的变量实例,所以不涉及线程安全的问题。
但是,如果这个变量是在线程需要使用的外部类中:
@Slf4j
public class Thread2 implements Runnable {
@Override
public void run() {
while (Counter.counter > 0) {
Counter.minus();
log.info("{}当前a值为:{}", Thread.currentThread().getName(), Counter.counter);
}
}
}
public class Counter {
public static int counter = 10;
public static void minus(){
count--;
}
}
此时就会出现线程安全问题了,针对这种情况,我们如何使得每个线程单独保留一份自己的变量实例呢?
主角ThreadLocal就该出现了。
二、入门实例
public class Counter {
public static ThreadLocal<Integer> counter = new ThreadLocal<>();
public static void initCounter(){
counter.set(10);
}
public static void minus(){
Integer cnt = counter.get();
counter.set(--cnt);
}
}
@Slf4j
public class Thread2 implements Runnable {
@Override
public void run() {
Counter.initCounter();
while (Counter.counter.get() > 0) {
Counter.minus();
log.info("{}当前a值为:{}", Thread.currentThread().getName(), Counter.counter.get());
}
}
}
这个例子中,每个线程都会保留一份属于自己的变量,相互之间不共享。
但是有一个问题,ThreadLocal变量声明后,默认get返回的是null,而不是0,我们有时候需要它能有默认值,那么就应该使用自定义的ThreadLocal类:
public class ThreadLocalNew<T> extends ThreadLocal {
@Override
protected Object initialValue() {
return 10;
}
}
public class Counter {
public static ThreadLocalNew<Integer> counter = new ThreadLocalNew<>();
public static void minus(){
Integer cnt = (Integer) counter.get();
counter.set(--cnt);
}
}
@Slf4j
public class Thread2 implements Runnable {
@Override
public void run() {
while ((Integer)Counter.counter.get() > 0) {
Counter.minus();
log.info("{}当前a值为:{}", Thread.currentThread().getName(), Counter.counter.get());
}
}
}
网友评论