1 简介
ThreadLocal使用线程存储信息。
2 Usage
2.1 常用方法
序号 | 方法 | 描述 |
---|---|---|
1 | set() | .添加值 |
2 | get() | 获取值 |
3 | remove() | 移除值 |
2.2 样例
package thread;
import java.util.logging.Logger;
/**
* ThreadLocal测试.
*
* @author xindaqi
* @since 2021/4/14 17:01
*/
public class ThreadLocalTest {
private static final Logger logger = Logger.getLogger("ThreadLocalTest");
public static void threadLocalNormal() {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("我是ThreadLocal存储的数据");
logger.info("Thread:" + Thread.currentThread().getName() + threadLocal.get());
threadLocal.remove();
}
public static void main(String[] args) {
ThreadLocalTest.threadLocalNormal();
}
}
- 结果
四月 15, 2021 3:50:02 下午 thread.ThreadLocalTest threadLocalNormal
信息: Thread:main我是ThreadLocal存储的数据
3 注意事项
3.1 父子线程通信
- 描述
子线程无法获取ThreadLocal父线程的数据。 - 样例
package thread;
import java.util.logging.Logger;
/**
* ThreadLocal测试.
*
* @author xindaqi
* @since 2021/4/14 17:01
*/
public class ThreadLocalTest {
private static final Logger logger = Logger.getLogger("ThreadLocalTest");
public static void threadLocalReadInAnotherThread() {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
logger.info("Thread:" + Thread.currentThread().getName());
threadLocal.set("我是主线程");
Thread thread1 = new Thread(() -> {
logger.info("Thread:" + Thread.currentThread().getName() + threadLocal.get());
threadLocal.remove();
});
thread1.start();
}
public static void main(String[] args) {
ThreadLocalTest.threadLocalReadInAnotherThread();
}
}
- 结果
四月 14, 2021 6:53:36 下午 thread.ThreadLocalTest threadLocalReadInAnotherThread
信息: Thread:main
四月 14, 2021 6:53:36 下午 thread.ThreadLocalTest lambda$threadLocalReadInAnotherThread$0
信息: Thread:Thread-1null
- 分析
ThreadLocal在主线程main设置变量,无法在子线程Thread-1获取,通过Thread-1获取的值为null。
3.2 解决父子线程通信
- 方案
使用InheritableThreadLocal解决父子线程通信问题。 - 样例
package thread;
import java.util.logging.Logger;
/**
* ThreadLocal测试.
*
* @author xindaqi
* @since 2021/4/14 17:01
*/
public class ThreadLocalTest {
private static final Logger logger = Logger.getLogger("ThreadLocalTest");
public static void threadLocalReadInAnotherThread() {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
logger.info("Thread:" + Thread.currentThread().getName());
threadLocal.set("我是主线程");
Thread thread1 = new Thread(() -> {
logger.info("Thread:" + Thread.currentThread().getName() + threadLocal.get());
threadLocal.remove();
});
thread1.start();
}
public static void inheritableThreadLocalReadInAnotherThread() {
ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
logger.info("Thread:" + Thread.currentThread().getName());
threadLocal.set("我是主线程");
Thread thread = new Thread(() -> {
logger.info("Thread:" + Thread.currentThread().getName() + threadLocal.get());
threadLocal.remove();
});
thread.start();
}
public static void threadLocalNormal() {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("我是ThreadLocal存储的数据");
logger.info("Thread:" + Thread.currentThread().getName() + threadLocal.get());
threadLocal.remove();
}
public static void main(String[] args) {
ThreadLocalTest.inheritableThreadLocalReadInAnotherThread();
}
}
- 结果
四月 14, 2021 6:55:35 下午 thread.ThreadLocalTest inheritableThreadLocalReadInAnotherThread
信息: Thread:main
四月 14, 2021 6:55:35 下午 thread.ThreadLocalTest lambda$inheritableThreadLocalReadInAnotherThread$1
信息: Thread:Thread-1我是主线程
- 解析
子线程Thread-1获取到主线程main的ThreadLocal设定的值。
3.3 线程池
使用线程池时,同一个线程复用,无法为ThreadLocal重新初始化,因此,读取的值一直是第一次赋的值。
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
/**
* ThreadLocal测试.
*
* @author xindaqi
* @since 2021/4/14 17:01
*/
public class ThreadLocalTest {
private static final Logger logger = Logger.getLogger("ThreadLocalTest");
public static void threadPoolWithInheritableThreadLocal() {
ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("我是主线程1");
ExecutorService exs = Executors.newFixedThreadPool(1);
exs.submit(() -> {
threadLocalValueSync(threadLocal);
});
threadLocal.set("我是主线程2");
exs.submit(() -> {
threadLocalValueSync(threadLocal);
});
threadLocalValueSync(threadLocal);
exs.shutdown();
}
public static synchronized void threadLocalValueSync(ThreadLocal threadLocal) {
logger.info("Thread:" + Thread.currentThread().getName() + threadLocal.get());
}
public static void main(String[] args) {
ThreadLocalTest.threadPoolWithInheritableThreadLocal();
}
}
- 结果
四月 14, 2021 9:48:07 下午 thread.ThreadLocalTest threadLocalValueSync
信息: Thread:pool-1-thread-1我是主线程1
四月 14, 2021 9:48:07 下午 thread.ThreadLocalTest threadLocalValueSync
信息: Thread:main我是主线程2
四月 14, 2021 9:48:07 下午 thread.ThreadLocalTest threadLocalValueSync
信息: Thread:pool-1-thread-1我是主线程1
- 分析
同一个线程复用,无法为ThreadLocal重新初始化,因此,读取的值一直是第一次赋的值:我是主线程1。
3.4 直接在线程池中创建ThreadLocal
package thread;
import java.util.concurrent.*;
import java.util.logging.Logger;
/**
* ThreadLocal测试.
*
* @author xindaqi
* @since 2021/4/14 17:01
*/
public class ThreadLocalTest implements Runnable {
private static int m = 0;
private static int n = 0;
private static final Logger logger = Logger.getLogger("ThreadLocalTest");
/**
* ThreadPoolExecutor线程池结合InheritableThreadLocal测试,
* 直接在线程池中使用ThreadLocal,避免父子线程传值问题
*/
public static void threadPoolExecutorWithInheritableThreadLocal() {
int corePoolSize = 3;
int maximumPoolSize = 5;
long keepAliveTime = 1000;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for(int i=0;i<4;i++) {
threadPoolExecutor.execute(new ThreadLocalTest());
}
threadPoolExecutor.shutdown();
}
/**
* 重写Runnable方法
*/
@Override
public void run() {
try{
/**
* 使用synchronized关键字
*/
ThreadLocal<String> threadLocalFromTest = testSync();
/**
* 不使用synchronized关键字
*/
// ThreadLocal<String> threadLocalFromTest = test();
n += 1;
threadLocalFromTest.set("我是主线程run:" + n);
logger.info("Thread run: " + Thread.currentThread().getName() + threadLocalFromTest.get());
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 使用InheritableThreadLocal创建线程存储
* synchronized保证原子性
*
* @return 线程存储对象
* @throws InterruptedException
*/
public static synchronized ThreadLocal<String> testSync() throws InterruptedException {
ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
m += 1;
threadLocal.set("我是主线程test:" + m);
logger.info("Thread test: " + Thread.currentThread().getName() + threadLocal.get());
return threadLocal;
}
/**
* 使用InheritableThreadLocal创建线程存储
*
* @return 线程存储对象
* @throws InterruptedException
*/
public static ThreadLocal<String> test() throws InterruptedException {
ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("我是主线程1");
logger.info("Thread test: " + Thread.currentThread().getName() + threadLocal.get());
return threadLocal;
}
public static void main(String[] args) {
ThreadLocalTest.threadPoolExecutorWithInheritableThreadLocal();
}
}
- 使用synchronized结果
四月 15, 2021 3:42:02 下午 thread.ThreadLocalTest testSync
信息: Thread test: pool-1-thread-1我是主线程test:1
四月 15, 2021 3:42:02 下午 thread.ThreadLocalTest run
信息: Thread run: pool-1-thread-1我是主线程run:1
四月 15, 2021 3:42:02 下午 thread.ThreadLocalTest testSync
信息: Thread test: pool-1-thread-3我是主线程test:2
四月 15, 2021 3:42:02 下午 thread.ThreadLocalTest run
信息: Thread run: pool-1-thread-3我是主线程run:2
四月 15, 2021 3:42:02 下午 thread.ThreadLocalTest testSync
信息: Thread test: pool-1-thread-2我是主线程test:3
四月 15, 2021 3:42:02 下午 thread.ThreadLocalTest run
信息: Thread run: pool-1-thread-2我是主线程run:3
四月 15, 2021 3:42:02 下午 thread.ThreadLocalTest testSync
信息: Thread test: pool-1-thread-1我是主线程test:4
四月 15, 2021 3:42:02 下午 thread.ThreadLocalTest run
信息: Thread run: pool-1-thread-1我是主线程run:4
- 不使用synchronized结果
四月 15, 2021 3:10:50 下午 thread.ThreadLocalTest test
信息: Thread test: pool-1-thread-3我是主线程1
四月 15, 2021 3:10:50 下午 thread.ThreadLocalTest test
信息: Thread test: pool-1-thread-2我是主线程1
四月 15, 2021 3:10:50 下午 thread.ThreadLocalTest test
信息: Thread test: pool-1-thread-1我是主线程1
四月 15, 2021 3:10:50 下午 thread.ThreadLocalTest run
信息: Thread run: pool-1-thread-2我是主线程2
四月 15, 2021 3:10:50 下午 thread.ThreadLocalTest run
信息: Thread run: pool-1-thread-3我是主线程2
四月 15, 2021 3:10:50 下午 thread.ThreadLocalTest run
信息: Thread run: pool-1-thread-1我是主线程2
- 分析
synchronized对同一个对象实现了原子性操作,当前线程执行结束,再进入下一个线程,多个线程即多个execute。在线程池中使用ThreadLocal即避免了同一线程复用而造成的无法更新线程存储的弊端。
4 源码分析
【参考文献】
[1]https://www.cnblogs.com/Nonnetta/p/10175662.html
[2]https://bbs.csdn.net/topics/390448707
[3]https://www.cnblogs.com/dafanjoy/p/9729358.html