ThreadLocal是什么
ThreadLocal提供线程局部变量。这些变量与普通的变量不同之处在于,每个访问这种变量的线程(通过它的get或set方法)都有自己的、独立初始化的变量副本。
ThreadLocal实例通常是希望将状态关联到一个线程的类的私有静态字段(比如,user ID 或者 Transaction ID 等等)。
(
画外音:这段话表达了三个意思
ThreadLocal是一种变量类型,我们称之为“线程局部变量”
每个线程访问这种变量的时候都会创建该变量的副本,这个变量副本为线程私有
- 每个ThreadLocal只能保存一个变量副本,如果想要上线一个线程能够保存多个副本以上,就需要创建多个ThreadLocal。
- ThreadLocal内部的ThreadLocalMap键为弱引用,会有内存泄漏的风险。
- 适用于无状态,副本变量独立后不影响业务逻辑的高并发场景。如果如果业务逻辑强依赖于副本变量,则不适合用ThreadLocal解决,需要另寻解决方案
- 应用场景: JDBC ,session链接等
比如最简单的JDBC,链接模拟如下,这种在多线程情况下,可能就会出现问题:
package book.thread.pool;
import static book.thread.pool.ThreadLocalCase.selectSql;
public class ThreadLocalCase {
public static Boolean isOpen = false;
/**
* 打开连接
*/
public static void openConnection() {
if (isOpen) {
System.out.println(Thread.currentThread().getName() + "打开链接,失败 isOpen= " + isOpen);
return;
}
isOpen = true;
System.out.println(Thread.currentThread().getName() + "打开链接成功 isOpen= " + isOpen);
}
/**
* 断开连接
*/
public static void closeConnection() {
if (isOpen) {
isOpen = false;
System.out.println(Thread.currentThread().getName() + "断开链接成功 isOpen=" + isOpen);
}
}
/**
* 执行sql
*/
public static void executeSql() throws InterruptedException {
if (isOpen) {
System.out.println(Thread.currentThread().getName() + "Sql 正在执行中......");
Thread.sleep(2000);
}
}
/**
* 执行
*/
public static void selectSql() throws InterruptedException {
openConnection();
executeSql();
closeConnection();
}
public static void main(String args[]) {
// selectSql();
for (int i = 0; i < 4; i++) {
ThreadLocalCaseJdBC stringThread = new ThreadLocalCaseJdBC();
Thread thread = new Thread(stringThread);
thread.start();
}
}
}
class ThreadLocalCaseJdBC implements Runnable {
@Override
public void run() {
// System.out.println("当前线程--->" + Thread.currentThread().getName());
try {
selectSql();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
-
结果如下:
这种情况下就必须要用到ThreadLocal了
代码修改为:
package book.thread.pool;
import static book.thread.pool.ThreadLocalCase.selectSql;
public class ThreadLocalCase {
private static ThreadLocal<Boolean> threadId = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return false;
}
};
/**
* 打开连接
*/
public static void openConnection() {
if (threadId.get()) {
System.err.println(Thread.currentThread().getName() + "打开链接,失败 isOpen= " + threadId.get());
return;
}
threadId.set(true);
System.out.println(Thread.currentThread().getName() + "打开链接成功 isOpen= " + threadId.get());
}
/**
* 断开连接
*/
public static void closeConnection() {
if (threadId.get()) {
threadId.set(false);
System.err.println(Thread.currentThread().getName() + "断开链接成功 isOpen=" + threadId.get());
}
}
/**
* 执行sql
*/
public static void executeSql() throws InterruptedException {
if (threadId.get()) {
System.out.println(Thread.currentThread().getName() + "Sql 正在执行中......");
Thread.sleep(2000);
}
}
/**
* 执行
*/
public static void selectSql() throws InterruptedException {
openConnection();
executeSql();
closeConnection();
}
public static void main(String args[]) {
for (int i = 0; i < 100; i++) {
ThreadLocalCaseJdBC stringThread = new ThreadLocalCaseJdBC();
Thread thread = new Thread(stringThread);
thread.start();
}
}
}
class ThreadLocalCaseJdBC implements Runnable {
@Override
public void run() {
// System.out.println("当前线程--->" + Thread.currentThread().getName());
try {
selectSql();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ThreadLocal每个线程单独赋值
package book.thread.pool;
import java.util.HashMap;
import java.util.Map;
import static book.thread.pool.ThreadId.printObject;
public class ThreadId {
public static final int MAP_MAX_SIZE = 10;
public static final int MAX_INITIALIZE_THREAD_COUNT = 100;
private static ThreadLocal<Map<String, String>> threadId = new ThreadLocal<Map<String, String>>() {
@Override
protected Map<String, String> initialValue() {
Map<String, String> map = new HashMap<>();
map.put(Thread.currentThread().getName(), Thread.currentThread().getName());
return map;
}
};
public static Map<String, String> get() {
return threadId.get();
}
public static Map<String, String> putMap() {
Map<String, String> map = threadId.get();
for (int i = 0; i < MAP_MAX_SIZE; i++) {
map.put(Thread.currentThread().getName() + ":" + i, String.valueOf(i));
}
return map;
}
public static Map<String, String> reMoveMap() {
Map<String, String> map = threadId.get();
for (int i = 0; i < MAP_MAX_SIZE; i++) {
map.remove(Thread.currentThread().getName() + ":" + i);
}
return map;
}
public static void printObject(String format,Object ...args) {
System.out.println(String.format(format, args));
}
public static void main(String args[]) {
for (int i = 0; i < MAX_INITIALIZE_THREAD_COUNT; i++) {
ThreadLocalMap threadLocalMap = new ThreadLocalMap();
Thread thread = new Thread(threadLocalMap);
thread.start();
}
}
}
class ThreadLocalMap implements Runnable {
@Override
public void run() {
try {
System.out.println("线程--->" + Thread.currentThread().getName() +"");
Thread.sleep(1000);
printObject("线程%s初始Map值为:%s",Thread.currentThread().getName(),ThreadId.get());
printObject("线程%s putMap值后为:%s",Thread.currentThread().getName(),ThreadId.putMap());
printObject("线程%s removeMap值后为:%s",Thread.currentThread().getName(),ThreadId.reMoveMap());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}