ThreadLocal源码分析|ThreadLocal源码分析,java的引用类型
ThreadLocal 使用实例
使用ThreadLocal创建的对象只能被当前线程访问,每个线程保存一个对象的副本,在多线程操作时是线程安全的。然后通过重写initialValue()方法,可以给初始值。每次调用get方法,先检查当前线程是否有这个THreadLocal对象,如果没有调用initialValue方法,或者返回null
上代码:
public class ThreadLocalTest {
public static void main(String[] args) throws InterruptedException {
final ThreadLocalTest test = new ThreadLocalTest();
test.set();
System.out.println(test.getLong());
System.out.println(test.getString());
// 在这里新建了一个线程
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
test.set();
//
System.out.println("Thread1==yield");
try{
Thread.currentThread().yield();
}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"==="+test.getLong());
System.out.println(Thread.currentThread().getName()+"==="+test.getString());
}
},"Thread1") ;
thread1.start();
System.out.println(test.getLong());
System.out.println(test.getString());
}ThreadLocal longLocal = new ThreadLocal();
ThreadLocal stringLocal = new ThreadLocal(){
@Override
protected String initialValue() {
return "";
}
};
public void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}public long getLong() {
return longLocal.get();
}public String getString() {
return stringLocal.get();
}}
输出:
1
main
1
main
Thread1==yield
Thread1===11
Thread1===Thread1
我们可以发现每个ThreadLocal变量,被放在线程的一个map中,然后get的时候,key就是ThreadLocal变量本身。也就是应该是这么个
map
。InheritableThreadLocal 使用实例
首先InheritableThreadLocal,继承自ThreadLocal. 但是这个是父线程创建的THreadLocal可以在子线程中使用,也可以在线程本身中使用。
上代码:
public class InheritableThreadLocalTest {
public static InheritableThreadLocal inheritableThreadLocal =
new InheritableThreadLocal();
public static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
inheritableThreadLocal.set("xxxxx");
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"==get"+
inheritableThreadLocal.get()+" from main Thread" );
}
});
try{
Thread.sleep(1000);
}catch (Exception e){}
inheritableThreadLocal.set("xxx2");
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"==get"+
inheritableThreadLocal.get()+" from main Thread" );
}
});
System.out.println(Thread.currentThread().getName()+"get==="+inheritableThreadLocal.get());
try{
Thread.sleep(2000);
}catch (Exception e){}
executorService.shutdown();
}
}
结果输出:
pool-1-thread-1==getxxxxx from main Thread
pool-1-thread-1==getxxxxx from main Thread
mainget===xxx2
【ThreadLocal源码分析|ThreadLocal源码分析,java的引用类型】我们可以发现,线程池中第二次提交的任务,还是获取的xxxxx ,并没有获取到xxx2 ,因为线程池中只有一个线程,并没有销毁掉,所以也就不会去初始化 thread1 get的时候,get的是自己的threadLocals变量
ThreadLocal源码分析
先看类的结构,类内部,有一个静态内部类,ThreadLocalMap,就是Thread类的threadLocals 引用的这个map对象.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
static class ThreadLocalMap {
static class Entry extends WeakReference> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = https://www.it610.com/article/v;
}
}
private Entry[] table;
private int threshold;
// Default to 0private Entry getEntry(ThreadLocal> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}private Entry getEntryAfterMiss(ThreadLocal> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
}
ThreadLocal核心主要是上述代码。首先set方法,先拿到当前线程的ThreadLocalMap,然后如果map!=null,就set进去。复杂的是get方法,在getEntry()时,如果没有命中的话,会触发expungeStaleEntry方法,回收key为null的Entry对象.由于Entry对象和ThreadLocal对象extends 软引用,当对象只有Softrefrence的时候,在内存不足的时候,会回收内存。
4种引用
Refrence 的子类
4种引用
我们都知道在Java中有4种引用,这四种引用从高到低分别为:
- StrongReference
- SoftReference
- WeakReference
WeakReference 基本与SoftReference 类似,只是回收的策略不同。
只要 GC 发现一个对象只有弱引用,则就会回收此弱引用对象。但是由于GC所在的线程优先级比较低,不会立即发现所有弱引用对象并进行回收。只要GC对它所管辖的内存区域进行扫描时发现了弱引用对象就进行回收。 - PhantomReference
主要是负责内存的一个状态,当然它还和java虚拟机,垃圾回收器打交道。Reference类首先把内存分为4种状态Active,Pending,Enqueued,Inactive。
- Active,一般来说内存一开始被分配的状态都是 Active,
- Pending 大概是指快要被放进队列的对象,也就是马上要回收的对象,
- Enqueued 就是对象的内存已经被回收了,我们已经把这个对象放入到一个队列中,方便以后我们查询某个对象是否被回收,
- Inactive就是最终的状态,不能再变为其它状态。
ReferenceQueue
引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到队列中,ReferenceQueue实现了入队(enqueue)和出队(poll),还有remove操作,内部元素head就是泛型的Reference。
推荐阅读
- 如何寻找情感问答App的分析切入点
- D13|D13 张贇 Banner分析
- 自媒体形势分析
- 2020-12(完成事项)
- Android事件传递源码分析
- Python数据分析(一)(Matplotlib使用)
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- 泽宇读书会——如何阅读一本书笔记
- Java内存泄漏分析系列之二(jstack生成的Thread|Java内存泄漏分析系列之二:jstack生成的Thread Dump日志结构解析)
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)