《Java 并发编程》专栏索引
《Java 并发编程》进程与线程
《Java 并发编程》共享模型之管程
《Java 并发编程》共享模型之内存
《Java 并发编程》共享模型之无锁
《Java 并发编程》共享模型之不可变
《Java 并发编程》线程池
例如,下面代码中的临界区
static int counter = 0
static void increment() {
// 临界区
counter++;
}
static void decrement() {
// 临界区
counter--;
}
多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件。
为了避免临界区的竞态条件发生,有多种手段可以达到目的。
这里使用阻塞式的解决方案:synchronized 来解决上述问题,即俗称的【对象锁】,它采用互斥的方式让同一时刻最多只有一个线程能持有【对象锁】,其他线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心上下文切换。
值得注意的是,虽然 Java 中互斥和同步都可以采用 synchronized 关键字来完成,但它们还是有区别的:
synchronized(对象) { //线程1,线程2(blocked)
临界区
}
案例代码
static int counter = 0;
//创建一个公共对象,作为对象锁的对象
static final Object room = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (room) {
counter++;
}
}
}, "t1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (room) {
counter--;
}
}
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
log.debug("{}",counter);
}
可以做这样的类比:
public class Test {
//在方法上加上synchronized关键字
public synchronized void test() {
}
//等价于
public void test() {
synchronized(this) {
}
}
}
public class Test {
//在静态方法上加上synchronized关键字
public synchronized static void test() {
}
//等价于
public void test() {
synchronized(Test.class) {
}
}
}
成员变量和静态变量是否线程安全?
局部变量是否安全?
局部变量线程安全性分析
public static void test1() {
int i = 10;
i++;
}
每个线程调用 test1() 方法时,局部变量 i 会在每个线程的栈帧内存中被创建多份,因此不存在共享,是线程安全的。
然而,局部变量的引用却有所不同,先看一个成员变量的例子
public class ThreadUnsafe {
static final int THREAD_NUMBER = 2;
static final int LOOP_NUMBER = 200;
ArrayList<String> list = new ArrayList<>();
public void method1(int loopNumber) {
for (int i = 0; i < loopNumber; i++) {
//{临界区,会产生竞态条件
method2();
method3();
//}
}
}
private void method2() {
list.add("1");
}
private void method3() {
list.remove(0);
}
public static void main(String[] args) {
ThreadUnsafe test = new ThreadUnsafe();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
test.method1(LOOP_NUMBER);
},"Thread" + i).start();
}
}
}
运行之后,可能有一种情况,method2 还未 add,method3 便开始 remove 就会报错:
Exception in thread "Thread0" Exception in thread "Thread1" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.remove(ArrayList.java:507)
at com.czh.concurrent.ThreadUnsafe.method3(ThreadUnsafe.java:26)
at com.czh.concurrent.ThreadUnsafe.method1(ThreadUnsafe.java:18)
at com.czh.concurrent.ThreadUnsafe.lambda$main$0(ThreadUnsafe.java:33)
at java.lang.Thread.run(Thread.java:748)
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.add(ArrayList.java:465)
at com.czh.concurrent.ThreadUnsafe.method2(ThreadUnsafe.java:23)
at com.czh.concurrent.ThreadUnsafe.method1(ThreadUnsafe.java:17)
at com.czh.concurrent.ThreadUnsafe.lambda$main$0(ThreadUnsafe.java:33)
at java.lang.Thread.run(Thread.java:748)
分析:
将 list 修改为局部变量
public class ThreadUnsafe {
static final int THREAD_NUMBER = 2;
static final int LOOP_NUMBER = 200;
public final void method1(int loopNumber) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < loopNumber; i++) {
//{临界区,会产生竞态条件
method2(list);
method3(list);
//}
}
}
private void method2(ArrayList<String> list) {
list.add("1");
}
private void method3(ArrayList<String> list) {
list.remove(0);
}
public static void main(String[] args) {
ThreadUnsafe test = new ThreadUnsafe();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
test.method1(LOOP_NUMBER);
},"Thread" + i).start();
}
}
}
分析:
方法修饰符带来的思考,如果把 method2 和 method3 的方法修改为 public 会不会带来线程安全?
public class ThreadUnsafe {
static final int THREAD_NUMBER = 2;
static final int LOOP_NUMBER = 200;
public final void method1(int loopNumber) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < loopNumber; i++) {
//{临界区,会产生竞态条件
method2(list);
method3(list);
//}
}
}
private void method2(ArrayList<String> list) {
list.add("1");
}
public void method3(ArrayList<String> list) {
list.remove(0);
}
public static void main(String[] args) {
ThreadUnsafe test = new ThreadUnsafe();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
test.method1(LOOP_NUMBER);
},"Thread" + i).start();
}
}
}
class ThreadSafeSubClass extends ThreadUnsafe {
@Override
public void method3(ArrayList<String> list) {
new Thread(()->{
list.remove(0);
});
}
}
从这个例子可以看出 private 或 final 提供【安全】的意义所在,体会开闭原则中的【闭】。
常见线程安全类
这里的线程安全是指,多个线程调用它们同一个实例的某个方法时,是线程安全的,也可以理解为:
Hashtable table = new Hashtable();
new Thread(()->{
table.put("key", ""value1);
}).start();
new Thread(()->{
table.put("key", "value2");
}).start();
Hashtable table = new Hashtable();
//线程1,线程2
if (table.get("key") == null) {
table.put("key", value);
}
不可变类线程安全性
当线程执行到临界区代码时,如果使用了 synchronized,会先查询 synchronized 中所指定的对象 (obj) 是否绑定了 Monitor.
注意:
Java 对象头格式
轻量级锁使用场景:当一个对象被多个线程所访问,但访问的时间是错开的(不存在竞争),此时就可以使用轻量级锁来优化。
创建锁记录(Lock Record)对象,每个线程的栈帧都会包含一个锁记录对象,内部可以存储锁定对象的 mark word(不在一开始就使用 Monitor)
让锁记录中的 Object Reference 指向锁对象(Object),并尝试用 CAS 去替换 Object 中的mark word,将此 mark word 放入 lock record 中保存
如果 CAS 替换成功,则将 Object 的对象头替换为锁记录的地址和状态 00(轻量级锁状态),并由该线程给对象加锁
重量级锁竞争时,还可以使用自旋来优化,如果当前线程在自旋成功(使用锁的线程退出了同步块,释放了锁),这时就可以避免线程进入阻塞状态。
轻量级锁在没有竞争时,每次重入(该线程执行的方法中再次锁住该对象)操作仍需要 CAS 替换操作,这样会导致性能降低。
所以引入了偏向锁对性能进行优化:在第一次 CAS 时会将线程的 ID 写入对象的 Mark Word中。此后发现这个线程 ID 就是自己的,就表示没有竞争,就不需要再次 CAS ,以后只要不发生竞争,这个对象就归该线程所有。
偏向状态
以下几种情况会使对象的偏向锁失效
当撤销偏向锁的阈值超过 40 以后,就会将整个类的对象都改为不可偏向的
wait 和 notify 都是线程之间进行协作的手段,都属于 Object 对象的方法,必须获得此对象的锁,才能调用这几个方法,示例代码如下:
public class Test {
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (obj) {
System.out.println("执行...");
try {
obj.wait(); //让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("其他代码...");
}).start();
new Thread(()->{
synchronized (obj) {
System.out.println("执行...");
try {
obj.wait(); //让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("其他代码...");
}
}).start();
//主线程两秒后执行
sleep(2);
System.out.println("唤醒 obj 上其他线程");
synchronized (obj) {
obj.notify(); //唤醒obj上一个线程
//obj.notifyAll(); //唤醒obj上所有等待线程
}
}
}
wait 和 sleep 的区别:
wait 与 sleep 的相同点:
什么时候适合使用 wait
使用 wait/notify 的注意点
synchronized (LOCK) {
while(//不满足条件,一直等待,避免虚假唤醒) {
LOCK.wait();
}
//满足条件后再运行
}
synchronized (LOCK) {
//唤醒所有等待线程
LOCK.notifyAll();
}
定义
保护性暂停(Guarded Suspension)用在一个线程等待另一个线程的执行结果。
要点:
案例代码如下
public class Test {
public static void main(String[] args) {
String hello = "hello thread!";
Guarded guarded = new Guarded();
new Thread(()->{
System.out.println("想要得到结果");
synchronized (guarded) {
System.out.println("结果是:"+guarded.getResponse());
}
System.out.println("得到结果");
}).start();
new Thread(()->{
System.out.println("设置结果");
synchronized (guarded) {
guarded.setResponse(hello);
}
}).start();
}
}
class Guarded {
/**
* 要返回的结果
*/
private Object response;
//优雅地使用 wait/notify
public Object getResponse() {
//如果返回结果为空就一直等待,避免虚假唤醒
while(response == null) {
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return response;
}
public void setResponse(Object response) {
this.response = response;
synchronized (this) {
//唤醒休眠的线程
this.notifyAll();
}
}
@Override
public String toString() {
return "Guarded{" +
"response=" + response +
'}';
}
}
带超时判断的暂停
public Object getResponse(long time) {
synchronized (this) {
//获取开始时间
long currentTime = System.currentTimeMillis();
//用于保存已经等待了的时间
long passedTime = 0;
while(response == null) {
//看经过的时间-开始时间是否超过了指定时间
long waitTime = time - passedTime;
if(waitTime <= 0) {
break;
}
try {
//等待剩余时间
this.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取当前时间
passedTime = System.currentTimeMillis()-currentTime
}
}
return response;
}
join 源码——使用保护性暂停模式
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
park/unpark 都是 LockSupport 类中的的方法
//暂停线程运行
LockSupport.park;
//恢复线程运行
LockSupport.unpark(thread);
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()-> {
System.out.println("park");
//暂停线程运行
LockSupport.park();
System.out.println("resume");
}, "t1");
thread.start();
Thread.sleep(1000);
System.out.println("unpark");
//恢复线程运行
LockSupport.unpark(thread);
}
与 Object 的 wait/notify 相比
每个线程都有一个自己的 park 对象,并且该对象由 _counter, _cond,__mutex 组成
情况 1:先调用 park,再调用 unpark
先调用 park
然后再调用 unpark
情况 2:先调用 unpark,再调用 park
先调用 unpark
再调用 park
情况一:NEW --> RUNNABLE
情况二:RUNNABLE <–> WAITING
情况三:RUNNABLE <–> WAITING
当前线程调用 t.join() 方法时,当前线程从 RUNNABLE –> WAITING
注意是当前线程在 t 线程对象的监视器上等待
t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 WAITING –> RUNNABLE 情况
情况四: RUNNABLE <–> WAITING
情况五: RUNNABLE <–> TIMED_WAITING
t 线程用 synchronized(obj) 获取了对象锁后
情况六:RUNNABLE <–> TIMED_WAITING
当前线程调用 t.join(long n) 方法时,当前线程从 RUNNABLE –> TIMED_WAITING
注意是当前线程在 t 线程对象的监视器上等待
当前线程等待时间超过了 n 毫秒,或 t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 TIMED_WAITING –> RUNNABLE
情况七:RUNNABLE <–> TIMED_WAITING
情况八:RUNNABLE <–> TIMED_WAITING
情况九:RUNNABLE <–> BLOCKED
情况十: RUNNABLE <–> TERMINATED
将锁的粒度细分
定义:因为某种原因,使得代码一直无法执行完毕,这样的现象叫做活跃性。
有这样的情况:一个线程需要同时获取多把锁,这时就容易发生死锁。
t1 线程获得 A 对象锁,接下来想获取 B 对象的锁, t2 线程获得 B 对象锁,接下来想获取 A 对象 的锁, 例:
public static void main(String[] args) {
final Object A = new Object();
final Object B = new Object();
new Thread(()->{
synchronized (A) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
}
}
}).start();
new Thread(()->{
synchronized (B) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (A) {
}
}
}).start();
}
发生死锁的四个必要条件
定位死锁的方法:
注意点:避免死锁要注意加锁顺序;另外如果由于某个线程进入了死循环,导致其它线程一直等待,对于这种情况 Linux 下可以通过 top 先定位到 CPU 占用高的 Java 进程,再利用 top -Hp 进程 id 来定位是哪个线程,最后再用 jstack 排查。
哲学家就餐问题
有 5 位哲学家,围坐在圆桌旁。他们只做两件事,思考和吃饭,思考一会吃口饭,吃完饭后接着思考。吃饭时要用两根筷子吃,桌上共有 5 根筷子,每位哲学家左右手边各有一根筷子。
如果筷子被身边的人拿着,自己就得等待。
筷子类
class Chopstick {
String name;
public Chopstick(String name) {
this.name = name;
}
@Override
public String toString() {
return "筷子{"+name+"}";
}
}
哲学家类
class Philosopher extends Thread {
Chopstick left;
Chopstick right;
public Philosopher(String name, Chopstick left, Chopstick right) {
super(name);
this.left = left;
this.right = right;
}
private void eat() {
System.out.println("eating...");
this.right = right;
}
@Override
public void run() {
while (true) {
//获得左手筷子
synchronized (left) {
//获得右手筷子
synchronized (right) {
//吃饭
eat();
}
//放下右手筷子
}
//放下左手筷子
}
}
}
避免死锁的方法
在线程使用锁对象时,顺序加锁即可避免死锁
活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束,例如
避免活锁的方法:在线程执行时,中途给予不同的间隔时间即可。
死锁与活锁的区别
某些线程因为优先级太低,导致一直无法获得资源的现象,在使用顺序加锁时,可能会出现饥饿现象。
和 synchronized 相比具有的的特点
基本语法
//获取ReentrantLock对象
private ReentrantLock lock = new ReentrantLock();
//加锁
lock.lock();
try {
//需要执行的代码
}finally {
//释放锁
lock.unlock();
}
可重入
可打断
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(()-> {
try {
//加锁,可打断锁
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
//被打断,返回,不再向下执行
return;
}finally {
//释放锁
lock.unlock();
}
});
lock.lock();
try {
t1.start();
Thread.sleep(1000);
//打断
t1.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
锁超时
不设置等待时间,立刻失败
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(()-> {
//未设置等待时间,一旦获取失败,直接返回false
if(!lock.tryLock()) {
System.out.println("获取失败");
//获取失败,不再向下执行,返回
return;
}
System.out.println("得到了锁");
lock.unlock();
});
lock.lock();
try{
t1.start();
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
设置等待时间
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(()-> {
try {
//判断获取锁是否成功,最多等待1秒
if(!lock.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("获取失败");
//获取失败,不再向下执行,直接返回
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
//被打断,不再向下执行,直接返回
return;
}
System.out.println("得到了锁");
//释放锁
lock.unlock();
});
lock.lock();
try{
t1.start();
//打断等待
t1.interrupt();
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
公平锁
//默认是不公平锁,需要在创建时指定为公平锁
ReentrantLock lock = new ReentrantLock(true);
条件变量
使用要点:
static Boolean judge = false;
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
//获得条件变量
Condition condition = lock.newCondition();
new Thread(()->{
lock.lock();
try{
while(!judge) {
System.out.println("不满足条件,等待...");
//等待
condition.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("执行完毕!");
lock.unlock();
}
}).start();
new Thread(()->{
lock.lock();
try {
Thread.sleep(1);
judge = true;
//释放
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
}
static final Object LOCK = new Object();
//判断先执行的内容是否执行完毕
static Boolean judge = false;
public static void main(String[] args) {
new Thread(()->{
synchronized (LOCK) {
while (!judge) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("2");
}
}).start();
new Thread(()->{
synchronized (LOCK) {
System.out.println("1");
judge = true;
//执行完毕,唤醒所有等待线程
LOCK.notifyAll();
}
}).start();
}
交替输出(wait/notify 版本)
public class Test {
static Symbol symbol = new Symbol();
public static void main(String[] args) {
new Thread(()->{
symbol.run("a", 1, 2);
}).start();
new Thread(()->{
symbol.run("b", 2, 3);
}).start();
symbol.run("c", 3, 1);
new Thread(()->{
}).start();
}
}
class Symbol {
public synchronized void run(String str, int flag, int nextFlag) {
for(int i=0; i<loopNumber; i++) {
while(flag != this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(str);
//设置下一个运行的线程标记
this.flag = nextFlag;
//唤醒所有线程
this.notifyAll();
}
}
/**
* 线程的执行标记, 1->a 2->b 3->c
*/
private int flag = 1;
private int loopNumber = 5;
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
public int getLoopNumber() {
return loopNumber;
}
public void setLoopNumber(int loopNumber) {
this.loopNumber = loopNumber;
}
}
ThreadLocal 是 JDK 包提供的,它提供线程本地变量,也就是如果创建了一个ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。
使用
public class ThreadLocalTest {
public static void main(String[] args) {
// 创建ThreadLocal变量
ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
// 创建两个线程,分别使用上面的两个ThreadLocal变量
Thread thread1 = new Thread(()->{
// stringThreadLocal第一次赋值
stringThreadLocal.set("thread1 stringThreadLocal first");
// stringThreadLocal第二次赋值
stringThreadLocal.set("thread1 stringThreadLocal second");
// userThreadLocal赋值
userThreadLocal.set(new User("Cristiano", 37));
// 取值
System.out.println(stringThreadLocal.get());
System.out.println(userThreadLocal.get());
// 移除
userThreadLocal.remove();
System.out.println(userThreadLocal.get());
});
Thread thread2 = new Thread(()->{
// stringThreadLocal第一次赋值
stringThreadLocal.set("thread2 stringThreadLocal first");
// stringThreadLocal第二次赋值
stringThreadLocal.set("thread2 stringThreadLocal second");
// userThreadLocal赋值
userThreadLocal.set(new User("Lionel", 34));
// 取值
System.out.println(stringThreadLocal.get());
System.out.println(userThreadLocal.get());
});
// 启动线程
thread1.start();
thread2.start();
}
}
class User {
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
thread1 stringThreadLocal second
thread2 stringThreadLocal second
User{name='Cristiano', age=37}
User{name='Lionel', age=34}
null
从运行结果可以看出
原理
public class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
...
}
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
Thread 类中有一个 threadLocals 和一个 inheritableThreadLocals,它们都是 ThreadLocalMap 类型的变量,而 ThreadLocalMap 是一个定制化的 Hashmap。在默认情况下,每个线程中的这两个变量都为 null.
ThreadLocal 中的方法
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获得ThreadLocalMap对象, 返回Thread类中的threadLocals
ThreadLocalMap map = getMap(t);
if (map != null)
//ThreadLocal自生的引用作为key,传入的值作为value
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
// 创建的同时设置想放入的值
// threadLocal自生的引用作为key,传入的值作为value
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
从 ThreadLocal 的源码可以看出,无论是 set、get、还是 remove,都是相对于当前线程操作
Thread t = Thread.currentThread();
因此 ThreadLocal 无法从父线程传向子线程,所以 InheritableThreadLocal 出现了,它能够让父线程中 ThreadLocal 的值传给子线程。
也就是从 main 所在的线程,传给 thread1 或 thread2
public class Test {
public static void main(String[] args) {
ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
InheritableThreadLocal<String> stringInheritable = new InheritableThreadLocal<>();
// 主线程赋对上面两个变量进行赋值
stringThreadLocal.set("this is threadLocal");
stringInheritable.set("this is inheritableThreadLocal");
// 创建线程
Thread thread1 = new Thread(()->{
// 获得ThreadLocal中存放的值
System.out.println(stringThreadLocal.get());
// 获得InheritableThreadLocal存放的值
System.out.println(stringInheritable.get());
});
thread1.start();
}
}
运行结果
null
this is inheritableThreadLocal
InheritableThreadLocal 的值成功从主线程传入了子线程,而 ThreadLocal 没有。
原理
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
// 传入父线程中的一个值,然后直接返回
protected T childValue(T parentValue) {
return parentValue;
}
// 返回传入线程的inheritableThreadLocals
// Thread中有一个inheritableThreadLocals变量
// ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
// 创建一个inheritableThreadLocals
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}