public class Test1 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
Test1 test1=new Test1();
for(int i=0;i<5;++i){
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
test1.setName(Thread.currentThread().getName()+"的数据");
System.out.println(Thread.currentThread().getName()+"的数据是"+test1.getName());
}
});
thread.setName("线程"+i);
thread.start();
}
}
}
/*打印结果:
线程1的数据是线程1的数据
线程0的数据是线程0的数据
线程2的数据是线程1的数据 //线程2先改了自己的工作内存,又刷新到了主内存的name的值。(只是一种情况)
线程3的数据是线程3的数据
线程4的数据是线程4的数据*/
一点都不想解释,懂的都懂。
下面是使用ThreadLocal的demo:
public class Test1 {
private String name;
static ThreadLocal<String> tlname=new ThreadLocal<>();
public static void main(String[] args) {
Test1 test1=new Test1();
for(int i=0;i<5;++i){
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
tlname.set(Thread.currentThread().getName()+"的数据");
System.out.println(Thread.currentThread().getName()+"的数据是"+tlname.get());
tlname.remove();
}
});
thread.setName("线程"+i);
thread.start();
}
}
}
/*打印结果:
线程2的数据是线程2的数据
线程1的数据是线程1的数据
线程0的数据是线程0的数据
线程3的数据是线程3的数据
线程4的数据是线程4的数据*/
比如开启多个线程执行service代码,由于数据库要用到事务(数据库里面的事务,不是Java里面的事务),而事务的逻辑是要在service层体现的,如(多线程执行service层的transfer方法)
至于为什么要保证是同一个connection:随便说其中一点,假如共享connection的话,若第一个线程里面抛出了异常,断掉了connection,剩下的线程就不能用了,这合理吗?
采用ThreadLocal,service层便不需要向dao层传connection,降低耦合度。也比上一种加锁的方法性能好。因为都是同一个线程的,所以不需要传connection。
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
protected T initialValue() { //ThreadLocalMap的value初始值为null
return null;//protected修饰该方法很明显就是为了可以被子类重写
}
public ThreadLocal() {
}
//返回当前线程的map里面找到key==this对应的value,如果value=null,
//也许是key不存导致vaule=null,也许是被回收了(这里挖坑)
//则将<this,initialValue>加入map
public T get() {
Thread t = Thread.currentThread();//获取当前线程对象
ThreadLocalMap map = getMap(t);//获取当前线程的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//将<this,initialValue>加入map,并返回initialVaule.
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
//将<this,value>加入到map
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//从map中将key==this的键值对移除
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
}
有几点比较重要的是:
ThreadLocalMap里的set/getEntry/remove方法中会调用expungeStaleEntry(),该方法会对key为null进行判断,key为null的话,也将value置为null。这意味着使用完ThreadLocal,CurrentThread已经运行的前提下,就算忘记调用remove方法,弱引用也比强引用可以多一层保障。弱引用的ThreadLocal会被回收,对应的value在下次一ThreadLocalMap调用set/get/remove任一方法时会被置为null,从而降低内存泄露的可能性。
因篇幅问题不能全部显示,请点此查看更多更全内容
怀疑对方AI换脸可以让对方摁鼻子 真人摁下去鼻子会变形
女子野生动物园下车狼悄悄靠近 后车司机按喇叭提醒
睡前玩8分钟手机身体兴奋1小时 还可能让你“变丑”
惊蛰为啥吃梨?倒春寒来不来就看惊蛰
男子高速犯困开智能驾驶出事故 60万刚买的奔驰严重损毁