ThreadLocal 原理及源码详解

什么是ThreadLocal?

ThreadLocal 是一种提供线程本地变量(也称为线程局部变量)的类,这种变量确保了在不同的线程中访问同一个 ThreadLocal 变量时,每个线程会有一个该变量的私有副本,即使多个线程修改了相同的 ThreadLocal 变量,每个线程所访问的副本仍然是独立的,从而避免了多线程环境下共享变量可能导致的数据不一致和竞争问题。

ThreadLocal 知识储备传送门:

ThreadLocal 实战使用详解

ThreadLocal 内存泄漏和常见问题详解

ThreadLocal 常用方法:

  • ThreadLocal #ThreadLocal():ThreadLocal 构造方法。
  • ThreadLocal #set(T value):设置当前线程绑定的局部变量。
  • ThreadLocal #get():获取当前线程绑定的局部变量。
  • ThreadLocal #remove():移出当前线程绑定的局部变量,防止内存泄漏。
  • ThreadLocal #initialValue():返回当前线程局部变量的初始值。

ThreadLocal 源码分析

ThreadLocal 内部结构:

  • 我们知道 ThreadLocal 是线程本地变量,那 ThreadLocal 肯定是线程的一个属性。
  • ThredLocal 类的内部有一个 ThreadLocalMap 的内部类,来帮我们进行的数据的存储。
  • ThreadLocalMap 的 key 是 ThreadLocal,value 是我们要操作的 value 值,我们可以把 ThreadLocalMap 理解为操作 ThreadLocal 的工具类。

在这里插入图片描述

我们知道 ThreadLocal 是属于 Thread 的,以下代码是摘自 Thread 源码中的一段:

class Thread implements Runnable {

    //与线程相关的 ThreadLocal值,此Map由 ThreadLocal 类维护
    ThreadLocal.ThreadLocalMap threadLocals = null;

    //与线程相关的InheritableThreadLocal 值。此Map 由 InheritableThreadLocal 类维护(子线程相关的 本次不讨论) 
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

可以看到我们本篇讨论的 ThreadLocal 出现了,其出现的地方是 Thread 类中,是 Thread 类的一个变量,ThreadLocal 出现的同时,我们发现了 ThreadLocalMap 的存在,根据 ThreadLocal.ThreadLocalMap 我们知道 ThreadLocalMap 是 ThreadLocal 的一个内部类,我们接着追踪一下 ThreadLocal 源码。

ThreadLocalMap#set(T value) 方法源码分析:

//往ThreadLocalMap 中设置 ThreadLocal 和 value 的关系
private void set(ThreadLocal<?> key, Object value) {
	//Entry 数组
	Entry[] tab = table;
	//获取数组的长度
	int len = tab.length;
	//返回下一个hashcode
	int i = key.threadLocalHashCode & (len-1);

	//遍历数组
	for (Entry e = tab[i];
		 e != null;
		 e = tab[i = nextIndex(i, len)]) {
		//获取key ThreadLocal
		ThreadLocal<?> k = e.get();
		//判断是否是当前key
		if (k == key) {
			//替换 value  返回
			e.value = value;
			return;
		}
		//key 为空
		if (k == null) {
			//key 为null 时候 清理过期的 entry 有助于 JVM GC
			replaceStaleEntry(key, value, i);
			return;
		}
	}
	
	//创建一个 entry 节点
	tab[i] = new Entry(key, value);
	//size 加1
	int sz = ++size;
	//是否大于等于负载因子 大于等于则需要扩容
	if (!cleanSomeSlots(i, sz) && sz >= threshold)
		//扩容
		rehash();
}



ThreadLocalMap#replaceStaleEntry 方法源码分析:

//把key value 设置到 staleSlot位置上 这里的key 为null
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
							   int staleSlot) {
	//获取当前	Entry 数组						   
	Entry[] tab = table;
	//数组长度
	int len = tab.length;
	//entry
	Entry e;


	int slotToExpunge = staleSlot;
	//从staleSlot 往前找entry 直到 (e = tab[i]) == null 结束
	for (int i = prevIndex(staleSlot, len);
		 (e = tab[i]) != null;
		 i = prevIndex(i, len))
		 //key为空 找到了需要废弃的位置了
		if (e.get() == null)
			slotToExpunge = i;

	从staleSlot 往后找entry 直到 (e = tab[i]) == null 结束
	for (int i = nextIndex(staleSlot, len);
		 (e = tab[i]) != null;
		 i = nextIndex(i, len)) {
		//(e = tab[i]) != null
		//获取key 
		ThreadLocal<?> k = e.get();

		//找到了自己 e 就是自己 e 的key 为 null  value 不为 null 
		if (k == key) {
			//替换 value 
			e.value = value;
			//交换 staleSlot 和 i 位置的 entry i 位置的entry 是 e 也是脏 entry 也是传过来的entry
			tab[i] = tab[staleSlot];
			//把当 entry e 也就是当前传过来的无效的脏的 entry的 key value 替换到 staleSlot 位置上 i 位置现在是个脏的无效的 entry
			tab[staleSlot] = e;
			
			if (slotToExpunge == staleSlot)
				//等于 表示 之前没有被改变过 也就是往前找 没有找到 方法入参 key 对应的entry 
				//因为当前遍历是从 staleSlot向后遍历 因此 i 大于 staleSlot   i 位置现在是个脏的无效的 entry 需求被清理掉 
				//赋值为 i 从i 的位置开始清理无效的脏的entry
				slotToExpunge = i;
			//遍历清除 脏的 也就是无效的 entry  expungeStaleEntry 方法 从 i 开始往后清理 无效的脏的 entry 
			cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
			return;
		}

		//key == null 表示找到了无效的数据 
		//如果此时 slotToExpunge 还是等于 staleSlot 说明在向前的循环中没有出现过期数据
		//i 赋值给 slotToExpunge
		if (k == null && slotToExpunge == staleSlot)
			slotToExpunge = i;
	}

	//走到这里表示没有清理无效的 脏的entry 设置 就直接设置  staleSlot 位置value 为空
	tab[staleSlot].value = null;
	//然后直接在把无效数据设置到 staleSlot 位置上 等待清理
	tab[staleSlot] = new Entry(key, value);
	
	//如果slotToExpunge == staleSlot 表示除了 staleSlot 位置上是过期数据之外 向前向后都没有找到过期数据
	//slotToExpunge 不等于 staleSlot 表示还有无效数据要清理
	if (slotToExpunge != staleSlot)
		cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}

ThreadLocalMap#expungeStaleEntry方法源码分析:

//从staleSlot位置开始往后找entry 遇到entry key为null 的设置其value 也为null 直到遇到entry 为null 结束 
private int expungeStaleEntry(int staleSlot) {
	//获取entry 数组
	Entry[] tab = table;
	//获取数组的长度
	int len = tab.length;

	// expunge entry at staleSlot
	//设置 staleSlot 位置的entry key 和value 都为 null 方便 JVM GC
	tab[staleSlot].value = null;
	tab[staleSlot] = null;
	//entry 数组长度-1
	size--;

	// Rehash until we encounter null
	Entry e;
	int i;
	//rehash 操作  (e = tab[i]) != null 才会继续循环 也就是 (e = tab[i]) == null 结束
	for (i = nextIndex(staleSlot, len);
		 (e = tab[i]) != null;
		 i = nextIndex(i, len)) {
		//获取entry 的key 也就是 ThreadLocal
		ThreadLocal<?> k = e.get();
		if (k == null) {
			//key 为 null 设置 value 也为 null
			e.value = null;
			tab[i] = null;
			//entry 数组长度-1
			size--;
		} else {
			//获取key 在数组中的位置
			int h = k.threadLocalHashCode & (len - 1);
			if (h != i) {
				//不是当前循环的位置 把当前位置设置为空
				tab[i] = null;
				
				// Unlike Knuth 6.4 Algorithm R, we must scan until
				// null because multiple entries could have been stale.
				//key 所在的位置不为空
				while (tab[h] != null)
					//递增找下一个为空的位置
					h = nextIndex(h, len);
				//为空  设置为 e
				tab[h] = e;
			}
		}
	}
	return i;
}

ThreadLocal #get() 源码分析:

get 方法逻辑很简单,就是获取当前线程绑定的局部变量。

//获取当前线程的局部变量
public T get() {
	//获取当前线程
	Thread t = Thread.currentThread();
	//获取当前线程的 ThreadLocalMap
	ThreadLocalMap map = getMap(t);
	if (map != null) {
		//map 不为空 获取当前 ThreadLocal 对应的 entry
		ThreadLocalMap.Entry e = map.getEntry(this);
		if (e != null) {
			@SuppressWarnings("unchecked")
			//获取value
			T result = (T)e.value;
			return result;
		}
	}
	//map 为空 创建一个map 设置初始值为 null 就是初始化操作
	return setInitialValue();
}

ThreadLocal #remove() 源码分析:

remove 方法就是移出当前线程绑定的局部变量,防止内存泄漏。

//删除当前线程的局部变量
//删除当前线程的局部变量
public void remove() {
	 //获取当前线程的 ThreadLocalMap
	 ThreadLocalMap m = getMap(Thread.currentThread());
	 if (m != null)
		 //不为空 执行删除操作
		 m.remove(this);
}

//根据 key 删除 entry
private void remove(ThreadLocal<?> key) {
	//entry 数组
	Entry[] tab = table;
	//数组的长度
	int len = tab.length;
	//找到 key 在entry 数组中的位置
	int i = key.threadLocalHashCode & (len-1);
	for (Entry e = tab[i];
		 e != null;
		 e = tab[i = nextIndex(i, len)]) {
		if (e.get() == key) {
			//找到key 删除掉 clear 方法其实就是赋值为 null 方便 JVM 回收
			e.clear();
			//清理无效的 entry 上面set 方法中有分析
			expungeStaleEntry(i);
			return;
		}
	}
}

ThreadLocal #cleanSomeSlots() 源码分析:

//清除无效数据
private boolean cleanSomeSlots(int i, int n) {
	boolean removed = false;
	Entry[] tab = table;
	int len = tab.length;
	do {
		//遍历数据
		i = nextIndex(i, len);
		Entry e = tab[i];
		//是否是过期数据
		if (e != null && e.get() == null) {
			n = len;
			removed = true;
			//清理过期数据
			i = expungeStaleEntry(i);
		}
	} while ( (n >>>= 1) != 0);
	return removed;
}

关于 ThreadLocal 的源码解析就分享到这里,本篇主要是对 ThreadLocal 的源码进行了分析,后面会接着分享 ThreadLocal 的使用及常用问题,希望可以帮助到有需要的伙伴。

欢迎提出建议及对错误的地方指出纠正。

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的方式案例演示(一)带返回值的方式00:17:12分钟 | 第8节多种创建线程的方式案例演示(二)使用线程池00:15:40分钟 | 第9节Spring对并发的支持:Spring的异步任务00:11:10分钟 | 第10节使用jdk8提供的lambda进行并行计算00:14:22分钟 | 第11节了解多线程所带来的安全风险00:13:16分钟 | 第12节从线程的优先级看饥饿问题00:18:42分钟 | 第13节从Java字节码的角度看线程安全性问题00:25:43分钟 | 第14节synchronized保证线程安全原理(理论层面) 00:13:59分钟 | 第15节synchronized保证线程安全原理(jvm层面)00:25:03分钟 | 第16节单例问题与线程安全性深入解析00:27:15分钟 | 第17节理解自旋锁,死锁与重入锁00:24:58分钟 | 第18节深入理解volatile原理与使用00:28:30分钟 | 第19节JDK5提供的原子类的操作以及实现原理00:27:10分钟 | 第20节Lock接口认识与使用00:19:54分钟 | 第21节手动实现一个可重入锁00:26:31分钟 | 第22节AbstractQueuedSynchronizer(AQS)详解00:49:04分钟 | 第23节使用AQS重写自己的锁00:31:04分钟 | 第24节重入锁原理与演示00:12:24分钟 | 第25节读写锁认识与原理00:18:04分钟 | 第26节细读ReentrantReadWriteLock源码00:30:38分钟 | 第27节ReentrantReadWriteLock锁降级详解00:13:32分钟 | 第28节线程安全性问题简单总结00:15:34分钟 | 第29节线程之间的通信之wait/notify00:32:12分钟 | 第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition源码00:21:15分钟 | 第34节实战:简易数据连接池00:24:53分钟 | 第35节线程之间通信之join应用与实现原理剖析00:10:17分钟 | 第36节ThreadLocal 使用及实现原理00:17:41分钟 | 第37节并发工具类CountDownLatch详解00:22:04分钟 | 第38节并发工具类CyclicBarrier 详解00:11:52分钟 | 第39节并发工具类Semaphore详解00:17:27分钟 | 第40节并发工具类Exchanger详解00:13:47分钟 | 第41节CountDownLatch,CyclicBarrier,Semaphore源码解析00:29:57分钟 | 第42节提前完成任务之FutureTask使用00:11:43分钟 | 第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器CopyOnWriteArrayList原理与使用00:15:52分钟 | 第48节并发容器ConcurrentLinkedQueue原理与使用00:31:03分钟 | 第49节Java中的阻塞队列原理与使用00:26:18分钟 | 第50节实战:简单实现消息队列00:11:07分钟 | 第51节并发容器ConcurrentHashMap原理与使用00:38:22分钟 | 第52节线程池的原理与使用00:42:49分钟 | 第53节Executor框架详解00:36:54分钟 | 第54节实战:简易web服务器(一)00:55:34分钟 | 第55节实战:简易web服务器(二)00:24:36分钟 | 第56节JDK8的新增原子操作类LongAddr原理与使用00:17:45分钟 | 第57节JDK8新增锁StampedLock详解00:29:37分钟 | 第58节重排序问题00:23:19分钟 | 第59节happens-before简单概述00:15:17分钟 | 第60节锁的内存语义00:13:54分钟 | 第61节volatile内存语义00:12:04分钟 | 第62节final域的内存语义00:34:07分钟 | 第63节实战:问题定位00:07:48分钟
第1节你真的了解并发吗? [免费观看][免费观看] 00:27:48分钟 | 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的方式案例演示(一)带返回值的方式00:17:12分钟 | 第8节多种创建线程的方式案例演示(二)使用线程池00:15:40分钟 | 第9节Spring对并发的支持:Spring的异步任务00:11:10分钟 | 第10节使用jdk8提供的lambda进行并行计算00:14:22分钟 | 第11节了解多线程所带来的安全风险00:13:16分钟 | 第12节从线程的优先级看饥饿问题00:18:42分钟 | 第13节从Java字节码的角度看线程安全性问题00:25:43分钟 | 第14节sy nchronized保证线程安全原理(理论层面)00:13:59分钟 | 第15节synchronized保证线程安全原理(jvm层面)00:25:03分钟 | 第16节单例问题与线程安全性深入解析00:27:15分钟 | 第17节理解自旋锁,死锁与重入锁00:24:58分钟 | 第18节深入理解volatile原理与使用00:28:30分钟 | 第19节JDK5提供的原子类的操作以及实现原理00:27:10分钟 | 第20节Lock接口认识与使用00:19:54分钟 | 第21节手动实现一个可重入锁00:26:31分钟 | 第22节AbstractQueuedSynchronizer(AQS)详解00:49:04分钟 | 第23节使用AQS重写自己的锁00:31:04分钟 | 第24节重入锁原理与演示00:12:24分钟 | 第25节读写锁认识与原理00:18:04分钟 | 第26节细读ReentrantReadWriteLock源码00:30:38分钟 | 第27节ReentrantReadWriteLock锁降级详解00:13:32分钟 | 第28节线程安全性问题简单总结00:15:34分钟 | 第29节线程之间的通信之wait/notify00:32:12分钟 | 第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition源码00:21:15分钟 | 第34节实战:简易数据连接池00:24:53分钟 | 第35节线程之间通信之join应用与实现原理剖析00:10:17分钟 | 第36节ThreadLocal 使用及实现原理00:17:41分钟 | 第37节并发工具类CountDownLatch详解00:22:04分钟 | 第38节并发工具类CyclicBarrier 详解00:11:52分钟 | 第39节并发工具类Semaphore详解00:17:27分钟 | 第40节并发工具类Exchanger详解00:13:47分钟 | 第41节CountDownLatch,CyclicBarrier,Semaphore源码解析00:29:57分钟 | 第42节提前完成任务之FutureTask使用00:11:43分钟 | 第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器CopyOnWriteArrayList原理与使用00:15:52分钟 | 第48节并发容器ConcurrentLinkedQueue原理与使用00:31:03分钟 | 第49节Java中的阻塞队列原理与使用00:26:18分钟 | 第50节实战:简单实现消息队列00:11:07分钟 | 第51节并发容器ConcurrentHashMap原理与使用00:38:22分钟 | 第52节线程池的原理与使用00:42:49分钟 | 第53节Executor框架详解00:36:54分钟 | 第54节实战:简易web服务器(一)00:55:34分钟 | 第55节实战:简易web服务器(二)00:24:36分钟 | 第56节JDK8的新增原子操作类LongAddr原理与使用00:17:45分钟 | 第57节JDK8新增锁StampedLock详解00:29:37分钟 | 第58节重排序问题00:23:19分钟 | 第59节happens-before简单概述00:15:17分钟 | 第60节锁的内存语义00:13:54分钟 | 第61节volatile内存语义00:12:04分钟 | 第62节final域的内存语义00:34:07分钟 | 第63节实战:问题定位00:07:48分钟 |
龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的方式案例演示(一)带返回值的方式00:17:12分钟 | 第8节多种创建线程的方式案例演示(二)使用线程池00:15:40分钟 | 第9节Spring对并发的支持:Spring的异步任务00:11:10分钟 | 第10节使用jdk8提供的lambda进行并行计算00:14:22分钟 | 第11节了解多线程所带来的安全风险00:13:16分钟 | 第12节从线程的优先级看饥饿问题00:18:42分钟 | 第13节从Java字节码的角度看线程安全性问题00:25:43分钟 | 第14节synchronized保证线程安全原理(理论层面)00:13:59分钟 | 第15节synchronized保证线程安全原理(jvm层面)00:25:03分钟 | 第16节单例问题与线程安全性深入解析00:27:15分钟 | 第17节理解自旋锁,死锁与重入锁00:24:58分钟 | 第18节深入理解volatile原理与使用00:28:30分钟 | 第19节JDK5提供的原子类的操作以及实现原理00:27:10分钟 | 第20节Lock接口认识与使用00:19:54分钟 | 第21节手动实现一个可重入锁00:26:31分钟 | 第22节AbstractQueuedSynchronizer(AQS)详解00:49:04分钟 | 第23节使用AQS重写自己的锁00:31:04分钟 | 第24节重入锁原理与演示00:12:24分钟 | 第25节读写锁认识与原理00:18:04分钟 | 第26节细读ReentrantReadWriteLock源码00:30:38分钟 | 第27节ReentrantReadWriteLock锁降级详解00:13:32分钟 | 第28节线程安全性问题简单总结00:15:34分钟 | 第29节线程之间的通信之wait/notify00:32:12分钟 | 第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition源码00:21:15分钟 | 第34节实战:简易数据连接池00:24:53分钟 | 第35节线程之间通信之join应用与实现原理剖析00:10:17分钟 | 第36节ThreadLocal 使用及实现原理00:17:41分钟 | 第37节并发工具类CountDownLatch详解00:22:04分钟 | 第38节并发工具类CyclicBarrier 详解00:11:52分钟 | 第39节并发工具类Semaphore详解00:17:27分钟 | 第40节并发工具类Exchanger详解00:13:47分钟 | 第41节CountDownLatch,CyclicBarrier,Semaphore源码解析00:29:57分钟 | 第42节提前完成任务之FutureTask使用00:11:43分钟 | 第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器CopyOnWriteArrayList原理与使用00:15:52分钟 | 第48节并发容器ConcurrentLinkedQueue原理与使用00:31:03分钟 | 第49节Java中的阻塞队列原理与使用00:26:18分钟 | 第50节实战:简单实现消息队列00:11:07分钟 | 第51节并发容器ConcurrentHashMap原理与使用00:38:22分钟 | 第52节线程池的原理与使用00:42:49分钟 | 第53节Executor框架详解00:36:54分钟 | 第54节实战:简易web服务器(一)00:55:34分钟 | 第55节实战:简易web服务器(二)00:24:36分钟 | 第56节JDK8的新增原子操作类LongAddr原理与使用00:17:45分钟 | 第57节JDK8新增锁StampedLock详解00:29:37分钟 | 第58节重排序问题00:23:19分钟 | 第59节happens-before简单概述00:15:17分钟 | 第60节锁的内存语义00:13:54分钟 | 第61节volatile内存语义00:12:04分钟 | 第62节final域的内存语义00:34:07分钟 | 第63节实战:问题定位00:07:48分钟
线程的状态以及各状态之间的转换详解.mp4 线程的初始化,中断以及其源码讲解.mp4 多种创建线程的方式案例演示(一)带返回值的方式.mp4 多种创建线程的方式案例演示(二)使用线程池.mp4 Spring对并发的支持:Spring的异步任务.mp4 使用jdk8提供的lambda进行并行计算.mp4 了解多线程所带来的安全风险.mp4 从线程的优先级看饥饿问题.mp4 从Java字节码的角度看线程安全性问题.mp4 synchronized保证线程安全原理(理论层面).mp4 synchronized保证线程安全原理(jvm层面).mp4 单例问题与线程安全性深入解析.mp4 理解自旋锁,死锁与重入锁.mp4 深入理解volatile原理与使用.mp4 JDK5提供的原子类的操作以及实现原理.mp4 Lock接口认识与使用.mp4 手动实现一个可重入锁.mp4 AbstractQueuedSynchronizer(AQS)详解.mp4 使用AQS重写自己的锁.mp4 重入锁原理与演示.mp4 读写锁认识与原理.mp4 细读ReentrantReadWriteLock源码.mp4 ReentrantReadWriteLock锁降级详解.mp4 线程安全性问题简单总结.mp4 线程之间的通信之wait notify.mp4 通过生产者消费者模型理解等待唤醒机制.mp4 Condition的使用及原理解析.mp4 使用Condition重写waitnotify案例并实现一个有界队列.mp4 深入解析Condition源码.mp4 实战:简易数据连接池.mp4 线程之间通信之join应用与实现原理剖析.mp4 ThreadLocal 使用及实现原理.mp4 并发工具类CountDownLatch详解.mp4 并发工具类CyclicBarrier 详解.mp4 并发工具类Semaphore详解.mp4 并发工具类Exchanger详解.mp4 CountDownLatch,CyclicBarrier,Semaphore源码解析.mp4 提前完成任务之FutureTask使用.mp4 Future设计模式实现(实现类似于JDK提供的Future).mp4 Future源码解读.mp4 ForkJoin框架详解.mp4 同步容器与并发容器.mp4 并发容器CopyOnWriteArrayList原理与使用.mp4 并发容器ConcurrentLinkedQueue原理与使用.mp4 Java中的阻塞队列原理与使用.mp4 实战:简单实现消息队列.mp4 并发容器ConcurrentHashMap原理与使用.mp4 线程池的原理与使用.mp4 Executor框架详解.mp4 实战:简易web服务器(一).mp4 实战:简易web服务器(二).mp4 JDK8的新增原子操作类LongAddr原理与使用.mp4 JDK8新增锁StampedLock详解.mp4 重排序问题.mp4 happens-before简单概述.mp4 锁的内存语义.mp4 volatile内存语义.mp4 final域的内存语义.mp4 实战:问题定位.mp4

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值