目录
createMap(Thread t, T firstValue)
Synchroized是Java内建的同步机制,它提供了互斥的语义和可见性,
当一个线程已经获取锁时,其他视图获取的线程只能阻塞在那里。
在加锁和解锁的过程中:
1、依赖于操作系统互斥锁【Mutex Lock】所实现的锁,消耗资源,属于重量级锁。
2、在Java1.6以前,另外在获取锁时,必须一直处于等待,没有额外的尝试机制
场景:
定义一个公共变量,在各自的线程中保存一个role角色,并取出role,
期望结果:各自拿到各自的role角色
阅读以下代码
public class Demo03 {
public static String role = "";
public static void show () {
String role = "";
System.out.println("show: "+Thread.currentThread().getName()+"分配角色:"+role);
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
role = "线程2中的角色-莫琪临";
show();
Sample.dosth();
}
}, "线程1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
role = "线程1中的角色-顾倾城";
show();
Sample.dosth();
}
}, "线程2");
t1.start();
t2.start();
}
}
class Sample{
public static void dosth () {
String role = Demo03.role;
System.out.println("dosth:"+Thread.currentThread().getName()+"分配角色:"+role);
}
}
结果:
分析:
公共变量的存储,后一个线程会覆盖公共变量值,导致最终结果达不到预期目标
想达到预期结果,需要使用ThreadLocal存储
ThreadLocal
作用:
每个线程自己独有的数据存储,仅供当前线程访问
理解:
数据是存储在该线程里的,只针对当前线程,属于当前线程局部的数据
修改上述代码 :
public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
都是通过threadLocal 来访问
set("名称") ------存储
get() -------取
public class Demo01 {
public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set("妲己");
show();
Sample.dosth();
}
}, "线程1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set("后羿");
show();
Sample.dosth();
}
}, "线程2");
t2.start();
}
public static void show () {
String role = threadLocal.get();
System.out.println("show: "+Thread.currentThread().getName()+"分配角色:"+role);
}
}
class Sample{
public static void dosth () {
String role = Demo01.threadLocal.get();
System.out.println("dosth:"+Thread.currentThread().getName()+"分配角色:"+role);
}
}
结果:
分析:
观察ThreadLocal类的set()方法
先获取当前线程
在获取当前线程的ThreadLocalMap:当前线程内部存储的键值对集合,在ThreadLocal中定义类型,在Thread类中应用。
所以ThreadLocal存储就是在该线程对象中,不能直接访问,通过ThreadLocal取。
可以这样理解:每个线程的内部都有一个键值对集合
总结:
作用一传递数据:
需要在不同线程中共享数据,将数据存储到Thread对象里【在每个Thread对象里都有ThreadLocalMap】,通过ThreadLocal来访问该数据【理解为中介】
ThreadLocal类中方法:
set(T val)
public void set(T value) {
//那个线程执行,就获取那个线程
Thread t = Thread.currentThread();
//得到ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);//以键值对的形式存储
} else {
createMap(t, value);//由于该map只是在Thread里面声明,所以第一次访问的时候需要创建
}
}
createMap(Thread t, T firstValue)
//传Thread t 是为了拿到threadLcoals的引用
void createMap(Thread t, T firstValue) {
//接受两个参数,实例化
//this ---Threalocal调用this就是该ThreadLocal
//val ---值
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
理解:
线程Thread对象,以参数t为例:
t 的内部有一个ThreadLocalMap,该Map的引用名叫做ThreadLocals,在定义时指向null,
现在需要将它new出来,进行实例化。
该方法传进来的是 t 线程,就完成 t 线程中ThreadLocalMap的创建
ThreadLocalMap的构造方法中传递两个值【和HashMap很像】:
一个线程里面可以有多个ThreadLocal,ThreadLocal自己充当这个Key【谁存谁来作键】,
【理解】用银行卡存钱,用该卡取钱
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//完成数组的初始化INITIAL_CAPACITY=16
table = new Entry[INITIAL_CAPACITY];
//计算下标
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//键值对存储 --
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
T get()
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//getEntry()---根据当前键得到当前键值对
//键是不同的ThreadLocal,
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
getEntry(ThreadLocal<?> key)
private Entry getEntry(ThreadLocal<?> key) {
//计算下标
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];//得到Entry键值对
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}