在java并发编程中,经常用到ThreadLocal来保存一个变量的值,而且,不同线程直接存放的数据具有隔离性。
ThreadLocal-set(T)/get()
通过set(T)和get()进行存值和取值,不同线程之间具有隔离性。
public class T{
public static ThreadLocal<String> tl = new ThreadLocal<String>();
public static void main(String[] args) {
if(tl.get() == null){
tl.set("main value");
}
new Thread(new MyThread()).start();
System.out.println(T.tl.get());
}
}
class MyThread implements Runnable{
@Override
public void run() {
if(T.tl.get() == null){
T.tl.set(Thread.currentThread().getName());
}
System.out.println(T.tl.get());
}
}
main value
Thread-0
ThreadLocal的initialValue()方法
如果在ThreadLocal的子类中使用initialValue()方法,那么new出的子类就要会直接使用该构造的值。也就是说,不进行set()也能直接通过get()提取到初始值。
public class T{
public static MyThreadLocal tl = new MyThreadLocal();
public static void main(String[] args) {
System.out.println(T.tl.get());
new Thread(new Mythread()).start();
}
}
class MyThreadLocal extends ThreadLocal<String>{
@Override
protected String initialValue() {
return "ThreadLocal的初始值";
}
}
class Mythread implements Runnable{
@Override
public void run() {
System.out.println(T.tl.get());;
}
}
ThreadLocal的初始值
ThreadLocal的初始值
继承父线程的ThreadLocal的值
有时候,需要让子线程继承父线程的某个值,这里的父子关系不是通过extends产生的父子关系,而是子线程在父线程启动产生的父子关系,举个例子,比如让t1在main线程只中启动,那么t1就是main线程的子线程。
public class T{
public static MyThreadLocal tl = new MyThreadLocal();
public static void main(String[] args) {
T.tl.set("asd");
System.out.println(T.tl.get());
new Thread(new Mythread()).start();
}
}
class MyThreadLocal extends InheritableThreadLocal<String>{
@Override
protected String initialValue() {
return "ThreadLocal的初始值";
}
/*
@Override
protected String childValue(String parentValue) {
return "child中的值";
}
*/
}
class Mythread implements Runnable{
@Override
public void run() {
System.out.println(T.tl.get());;
}
}
asd
asd
你会发现,在main中修改了ThreadLocal中的值后,子线程中的值也会随着改变,当然,如果你的修改在子线程启动之后才完成的,那么这个值将不会影响到子线程的ThreadLocal中的值。
childValue()
如果希望子线中的子不继承于父线程,而使用指定的值,那么这里应该使用childValue()方法来实现对子线程值的ThreadLocal中的值进行初始化。
public class T{
public static MyThreadLocal tl = new MyThreadLocal();
public static void main(String[] args) {
T.tl.set("asd");
System.out.println(T.tl.get());
new Thread(new Mythread()).start();
}
}
class MyThreadLocal extends InheritableThreadLocal<String>{
@Override
protected String initialValue() {
return "ThreadLocal的初始值";
}
@Override
protected String childValue(String parentValue) {
return "child中的值";
}
}
class Mythread implements Runnable{
@Override
public void run() {
System.out.println(T.tl.get());;
}
}
输出结果:
asd
child中的值
ThreadLocal的应用场景
在Spring中,dao和service都是单例,当多个用户并发的时候,可能产生多个连接,所以,有的读者认为,dao不应该是单例,但事实上在Spring容器中,dao确实是单例,那么他是如何实现一个单例保存多个connection的呢?实际上,Spring就是用到了ThreadLocal。下面来看一下它的实现方式。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class T{
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
public static Connection getConnection() throws ClassNotFoundException, SQLException{
Connection connection = tl.get();
if(connection == null){
Class.forName("com.mysql.jdbc.Driver");
String url = "";
String username = "";
String password = "";
connection = DriverManager.getConnection(url,username,password);
tl.set(connection);
}
return connection;
}
}
在单例模式下,不同线程都可以通过ThreadLocal能获取到自身的Connection。