This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
For example, the class below generates unique identifiers local to each thread. A thread's id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
Since:
1.2
Author:
Josh Bloch and Doug Lea
ThreadLocal类用来提供县城内部的局部变量 这种局部变量在多线程环境下访问(通过get 和 set 方法访问)时能保证各个线程的变量相对独立于其他线程内的变量 ThreadLocal实例通常来说都是 private static 类型的,用于关联线程和线程上下文
在Java中,ThreadLocal是一个用于创建线程局部变量的类。它允许我们在每个线程中创建一个变量的副本,这意味着在不同的线程中,即使它们使用相同的代码和资源,也可以拥有不同的副本。ThreadLocal的主要作用是提供了一种线程安全的方式来存储和访问非共享的数据,从而避免了线程间数据竞争的问题。
具体来说,当我们在多线程应用程序中使用共享的对象或变量时,可能会导致问题,因为多个线程同时修改同一份数据可能会导致数据的不一致性或错误状态。使用ThreadLocal,我们可以为每个线程创建独立的变量,从而确保它们之间的数据不会相互干扰。这种方式可以提高应用程序的并发性能和可靠性。
例如,在一个Web应用程序中,每个请求都会被一个独立的线程处理。如果我们需要存储与请求相关的数据,例如登录用户信息、请求参数等等,我们可以使用ThreadLocal来创建一个变量的副本,每个线程都可以独立地访问和修改这些数据,而不会影响其他线程的数据。
总之,ThreadLocal在Java中的作用是提供了一种线程安全的方式来存储和访问非共享数据,是多线程应用程序中常用的一种技术。
总结:
1. 线程并发
2. 传递数据 我们可以通过ThreadLocal在同一线程,不同组件中传递公共对象
3. 线程隔离 每个线程的变量都是独立的,不会被其他线程影响
先来看一个例子
public class MyDemo01 {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static void main(String[] args) {
MyDemo01 demo01 = new MyDemo01();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
demo01.setContent(Thread.currentThread().getName() + "的数据");
System.out.println("--------------------------------");
System.out.println(Thread.currentThread().getName()+"--->"+demo01.getContent());
}
});
thread.setName("线程"+i);
thread.start();
}
}
}
由于线程抢占式执行的特点,线程拿到了不该是本线程的数据,怎么解决呢?
/*
* 需求: 线程隔离
* 在多线程并发条件下,每个线程中的变量是独立的
* */
public class MyDemo01 {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
private String content;
public String getContent() {
//return content;
//返回当前线程绑定的变量
String s = threadLocal.get();
return s;
}
public void setContent(String content) {
//this.content = content;
//将变量content绑定到当前线程
threadLocal.set(content);
}
public static void main(String[] args) {
MyDemo01 demo01 = new MyDemo01();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
demo01.setContent(Thread.currentThread().getName() + "的数据");
System.out.println("--------------------------------");
System.out.println(Thread.currentThread().getName()+"--->"+demo01.getContent());
}
});
thread.setName("线程"+i);
thread.start();
}
}
}
加入ThreadLocal成员变量后,就解决了这个问题
解决了线程隔离的需求
public class MyDemo02 {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static void main(String[] args) {
MyDemo02 demo01 = new MyDemo02();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (MyDemo02.class){
demo01.setContent(Thread.currentThread().getName() + "的数据");
System.out.println("--------------------------------");
System.out.println(Thread.currentThread().getName()+"--->"+demo01.getContent());
}
}
});
thread.setName("线程"+i);
thread.start();
}
}
}
我们发现使用加锁操作也实现了线程隔离的需求啊!
但是仔细观察两个结果,会发现加锁实现的更加工整(虚线输出)
加锁使得结果更加串行化了,这样好不好呢?
我们首先要知道,加锁操作势必会降低效率的
加锁意味着我们的线程要排着队进行访问
虽然两者都用于处理多线程并发访问变量的问题,不过两者处理问题的角度和思路不同
事务的使用的注意点:
service层和dao层的连接对象要保持一致
每个线程的connection对象必须前后一致,线程隔离
解决方案:
传参,将service层的对象传递给dao层
加锁
使用ThreadLocal
前两种方案的弊端:
提高代码的耦合度
降低程序性能,必须排队执行
JDK早期的ThreadLocal是这样设计的:
每个ThreadLocal都创建一个Map
然后线程作为key
存储的变量作为value
这样就能达到各个线程的局部变量隔离的效果
现在每一个线程中,都会有一个成员变量ThreadLocalMap
现在方案的好处:
在分析ThreadLocal方法的时候,我们了解到ThreadLocal的操作实际上是围绕ThreadLocalMap展开的
ThreadLocalMap类在ThreadLocal中定义,但实例是由Thread对象维护的