前提须知:ThreadLocal并不是用来解决多线程共享安全问题的!
网上的例子真的不能全信!有的例子2个,3个线程跑没问题,但是一旦达到一定数量则会出现问题!
本人通过2个例子:
第一个例子用来告诉你:ThreadLocal并不能解决多线程共享安全问题
设计思路:创建一个ChangeInfo对象并设置name=“kkkk”,
起5个线程跑每个线程都把ChangeInfo对象保存在ThreadLocal中,
然后在从ThreadLocal中读取当前线程ChangeInfo对象的name值为,
然后再次更改ChangeInfo对象的name值为“aaaaa”。
【如果说ThreadLocal能够解决多线程问题,那么每个线程最终的输出都是先输出:kkkk,
然后在输出:aaaaa|||||||如果说ThreadLocal不能够解决多线程问题,
那么当上一个线程改变了ChangeInfo对象的name值后,后面的线程两次输出则都是aaaaa】
第二个例子:对于问题1如果来解决方案
public class ChangeInfo {
private String name;
public ChangeInfo(String str)
{
this.name=str;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Demo implements Runnable {
private ChangeInfo changeInfo;
final ThreadLocal<ChangeInfo> local=new ThreadLocal<ChangeInfo>();
//通过构造方法把对象传过来
public Demo(ChangeInfo changeInfo){
this.changeInfo=changeInfo;
}
@Override
public void run() {
//先把对象引用放到ThreadLocal中保存
local.set(changeInfo);
//输出本线程中对象name属性
System.out.println(Thread.currentThread().getName()+"该线程设置值之前>>>"+local.get().getName()+"|||||||||"+changeInfo);
//更改本线程对象的Name属性
local.get().setName("aaaaaaaaaa");
System.out.println(Thread.currentThread().getName()+"该线程设置值之后来+++>>>"+local.get().getName()+"|||||||||"+changeInfo);
}
public class Test {
public static void main(String[] args) throws InterruptedException {
ChangeInfo ChangeInfo=new ChangeInfo("KKKKK");
Demo d=new Demo(ChangeInfo);
Thread t1=new Thread(d);
Thread t2=new Thread(d);
Thread t3=new Thread(d);
Thread t4=new Thread(d);
Thread t5=new Thread(d);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
运行结果如下:可以看出threadLocal Copy的是每个对象的【引用地址】,你每次都是修改同1个
地址的对象,你说能实现隔离作用吗???
Thread-0该线程设置值之前>>>KKKKK|||||||||com.single.ChangeInfo@b9e45a
Thread-1该线程设置值之前>>>KKKKK|||||||||com.single.ChangeInfo@b9e45a
Thread-0该线程设置值之后来+++>>>aaaaaaaaaa|||||||||com.single.ChangeInfo@b9e45a
Thread-1该线程设置值之后来+++>>>aaaaaaaaaa|||||||||com.single.ChangeInfo@b9e45a
Thread-3该线程设置值之前>>>aaaaaaaaaa|||||||||com.single.ChangeInfo@b9e45a
Thread-2该线程设置值之前>>>aaaaaaaaaa|||||||||com.single.ChangeInfo@b9e45a
Thread-2该线程设置值之后来+++>>>aaaaaaaaaa|||||||||com.single.ChangeInfo@b9e45a
Thread-4该线程设置值之前>>>aaaaaaaaaa|||||||||com.single.ChangeInfo@b9e45a
Thread-4该线程设置值之后来+++>>>aaaaaaaaaa|||||||||com.single.ChangeInfo@b9e45a
Thread-3该线程设置值之后来+++>>>aaaaaaaaaa|||||||||com.single.ChangeInfo@b9e45a
那么如何解决上述问题?
下面对上述例子做修改,ChangeInfo这个类不用修改,主要修改其他2个类。
public class Demo implements Runnable {
/*
private ChangeInfo changeInfo;
final ThreadLocal<ChangeInfo> local=new ThreadLocal<ChangeInfo>();
//通过构造方法把对象传过来
public Demo(ChangeInfo changeInfo){
this.changeInfo=changeInfo;
}*/
@Override
public void run() {
//先把对象引用放到ThreadLocal中保存
// local.set(changeInfo);
//输出本线程中对象name属性
System.out.println(Thread.currentThread().getName()+"该线程设置值之前>>>"+Test.localThread.get().getName());
//更改本线程对象的Name属性
// local.get().setName("aaaaaaaaaa");
Test.localThread.get().setName("aaaaaaaa");
System.out.println(Thread.currentThread().getName()+"该线程设置值之后来+++>>>"+Test.localThread.get().getName());
}
public class Test {
static ThreadLocal<ChangeInfo> localThread=new ThreadLocal<ChangeInfo>(){
@Override
protected ChangeInfo initialValue() {
return new ChangeInfo("KKKKK");
}
};
public static void main(String[] args) throws InterruptedException {
//ChangeInfo ChangeInfo=new ChangeInfo("KKKKK");
// Demo d=new Demo(ChangeInfo);
Demo d=new Demo();
Thread t1=new Thread(d);
Thread t2=new Thread(d);
Thread t3=new Thread(d);
Thread t4=new Thread(d);
Thread t5=new Thread(d);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
运行结果如下,是不是实现了隔离的效果??建议大家自己看完之后一定要自己写一遍
Thread-0该线程设置值之前>>>KKKKK
Thread-0该线程设置值之后来+++>>>aaaaaaaa
Thread-1该线程设置值之前>>>KKKKK
Thread-1该线程设置值之后来+++>>>aaaaaaaa
Thread-3该线程设置值之前>>>KKKKK
Thread-3该线程设置值之后来+++>>>aaaaaaaa
Thread-2该线程设置值之前>>>KKKKK
Thread-2该线程设置值之后来+++>>>aaaaaaaa
Thread-4该线程设置值之前>>>KKKKK
Thread-4该线程设置值之后来+++>>>aaaaaaaa
1、ThreadLocal 并不解决多线程 共享 变量的问题。既然变量不共享,那就更谈不上同步的问题。
2、同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本实例
3、ThreadLocal 隔离的实现必要条件:set进入ThreadLocal 中应该是实例,而不应该是对象的引用!