01线程状态_java中的线程状态
![](https://i-blog.csdnimg.cn/blog_migrate/8a7934064f3d73100bed3b21d663d1ac.png)
![](https://i-blog.csdnimg.cn/blog_migrate/798fd8ca3c44257a77506a998d6d13d2.png)
NEW:刚刚创建(new)出的线程对象,仅仅是个java对象,没有跟操作系统底层关联,不会被操作系统分配给cpu执行。
RUNNABLE:调用start后,只有这个状态的线程代码才会得到cpu的执行。
TERMINATED:代码执行完毕,此时操作系统底层的线程和相关资源才会得到释放。
BLOCKED:获取锁失败时进入堵塞状态,获取锁成功后回到RUNNABLE状态。
WAITING:获得了锁但是代码执行的条件还不满足,所以调用wait方法释放锁。将来条件满足时另一个线程调用notify唤醒等待的线程。
TIMED_WAITING:有时限的版本。
02线程状态_java中的线程状态代码演示
03 五种状态vs六种状态
![](https://i-blog.csdnimg.cn/blog_migrate/f265fe562f8471f609441ee4bd112d4b.png)
新建、就绪、运行、堵塞、终结
04 线程池核心参数_简介
![](https://i-blog.csdnimg.cn/blog_migrate/9b9c101501c1b70e4235867d154bac19.png)
核心线程满了放到堵塞队列。
堵塞队列满了,此时救急线程出场(keepAliveTime + unit 时间内救急线程没有任务,救急线程就会从线程池中去掉)。
救急线程 也到上限了,此时就要用到拒绝策略。
05 线程池核心参数演示
![](https://i-blog.csdnimg.cn/blog_migrate/2a3be09080f94926120bf8caf477a10a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a701f0a1d3b34ff8a24a92c9bdc67de7.png)
救急线程执行完后发现堵塞队列中还有任务,就会把堵塞队列中的任务也执行掉
![](https://i-blog.csdnimg.cn/blog_migrate/ac67bd8e132a88f46c484d7119b0c1c2.png)
AbortPolicy()拒绝策略:直接抛异常
![](https://i-blog.csdnimg.cn/blog_migrate/0798eab6e7b989b7c708d6d1920c3a7b.png)
CallerRunsPolicy()拒绝策略:由主线程运行
![](https://i-blog.csdnimg.cn/blog_migrate/9e1fbce9478a75a16f2df4d5412665d9.png)
DiscardPolicy():直接把这个任务丢弃
DiscardOldestPolicy():把等待最久的任务从任务队列中丢弃,把新的任务放入队列。
![](https://i-blog.csdnimg.cn/blog_migrate/2e08b3dc4187b8d5953bc5bf23d3075c.png)
06 wait vs sleep
![](https://i-blog.csdnimg.cn/blog_migrate/de9a42497147cfa125239c8d2dc56625.png)
打断唤醒:拿到线程对象的话可以使用interrupt方法打断
07 wait sleep演示
![](https://i-blog.csdnimg.cn/blog_migrate/64de7c0b7f9b3b684752950738d42317.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4238979dd30f58420e4e5b7363dc8a69.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b56075ce45ae5e5a79fb01ed5d4ced36.png)
08 lock vs synchronized
![](https://i-blog.csdnimg.cn/blog_migrate/a9170e70a674912da50640b86e002762.png)
Lock对象可以知道哪些线程在这个锁对象上等待。公平锁的效率不如非公平锁。
09 lock_堵塞演示
还未加锁时的Lock
![](https://i-blog.csdnimg.cn/blog_migrate/f076da2265d9de9db30f26d554e7b698.png)
t1、t2、t3、t4线程依次来获取Lock
t1获得Lock时
![](https://i-blog.csdnimg.cn/blog_migrate/f03f915bb71096ed2ca39fc249de024a.png)
t1释放锁后
![](https://i-blog.csdnimg.cn/blog_migrate/cab436e7d1a3534b89e3854c3343e312.png)
t2释放锁后
![](https://i-blog.csdnimg.cn/blog_migrate/f17de0e7d5c8532a64dcefce8807696c.png)
不是默认非公平锁吗,怎么执行顺序还是跟获取锁的顺序相同,那这个非公平体现在哪?
10 lock_公平非公平演示
因为上面是所有线程都在堵塞队列中的情况
![](https://i-blog.csdnimg.cn/blog_migrate/8f4d280eb60ffb85f66da6cab2840f45.png)
![](https://i-blog.csdnimg.cn/blog_migrate/98a4e3013e33a875c0852e2493ef1b72.png)
11 lock_条件变量演示
await后到等待队列中等待
![](https://i-blog.csdnimg.cn/blog_migrate/e4c8c9b262721ac91ce02f843c8fce3e.png)
signAll后唤醒的线程到堵塞队列尾部
![](https://i-blog.csdnimg.cn/blog_migrate/86ca77ec383c5244839939f987b33c20.png)
12 volatile 举例
![](https://i-blog.csdnimg.cn/blog_migrate/521dcfc01fd999ef20eb6a4d76829094.png)
13 volatile 原子性 演示
![](https://i-blog.csdnimg.cn/blog_migrate/d9746186b5f21f0c90292636ed556185.png)
14 volatile 可见性 演示
![](https://i-blog.csdnimg.cn/blog_migrate/9dfb721376d40ea7f7ce06b37ae37c79.png)
主线程并没有停下来
15 volatile 可见性 原因与解决
![](https://i-blog.csdnimg.cn/blog_migrate/01e8516d25d13696e32086b9af5c24ff.png)
发现stop从内存中读了几十万次都是false,jit会把这个stop变量直接替换为false
![](https://i-blog.csdnimg.cn/blog_migrate/acc2d0c6284f5d02f9c8d0a837fbc5e7.png)
对变量加上volatile后jit就不会对这个变量进行优化。
16 volatile 有序性 分析
![](https://i-blog.csdnimg.cn/blog_migrate/dac11f2a4e132502e389ac03fe77c65b.png)
17
![](https://i-blog.csdnimg.cn/blog_migrate/c163d0716e4364271181c974c5094375.png)
18 volatile 有序性 volatile位置不同分析
![](https://i-blog.csdnimg.cn/blog_migrate/adfc687e74f2afc40c0da3623c60d1e8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7b45bf3190765de7f09a4bf7177d110b.png)
volatile修饰的变量在写时会加一个向上的写屏障,防止上面的代码跑下来
volatile修饰的变量在读时会加一个向下的读屏障,防止下面的代码跑上来
但是如果volatile加在x上,达不到防止指令重排序的效果
![](https://i-blog.csdnimg.cn/blog_migrate/87e670a55eecb0445c7388d4ecd07336.png)
19 悲观锁、乐观锁
![](https://i-blog.csdnimg.cn/blog_migrate/36853b6dd6e4dbb909f831534f945082.png)
20 悲观锁_乐观锁_unsafe_cas
![](https://i-blog.csdnimg.cn/blog_migrate/6fc2fd430370cf557ded022a852760d9.png)
cas的对象必须加volatile,因为cas能保证原子性,但是我们还要保证所操作的对象是可见的。
21 悲观锁_乐观锁_代码对比
悲观锁
![](https://i-blog.csdnimg.cn/blog_migrate/6927a946cae8346daa5821614fa0c145.png)
乐观锁
![](https://i-blog.csdnimg.cn/blog_migrate/c9c4e0b2e067774cc1a8c36c18d95bf8.png)
22 Hashtable vs concurrenthashmap
![](https://i-blog.csdnimg.cn/blog_migrate/d82a82454bb7a2661ea4ec533a225d29.png)
23 hashtable演示
不需要二次哈希
24 concurrenthashmap jdk 1.7
segment + 数组(Map.Entry结构) + 链表
演示并发度
![](https://i-blog.csdnimg.cn/blog_migrate/534113bb4eabe9942756a4c085632aec.png)
clevel:并发度,即有几个segment
capacity:capacity / clevel 即一个segment下的数组有多大,如果capacity<clevel(segment下的数组长度最小为2)
演示索引计算
![](https://i-blog.csdnimg.cn/blog_migrate/f2345f82e871a15f6d837b71346a2292.png)
segment下标的计算:二次哈希后得到的值对应的二进制数取前n位(2^n=clevel)对应的十进制值。
小数组下标的计算,低n位对应的十进制值,(2^n = min(2, capacity / clevel)
演示扩容
segment的容量是不能扩容的,在构造函数中确定了并发度后就一直不会变。
![](https://i-blog.csdnimg.cn/blog_migrate/841f15c450f9b1409a52164129bcfdde.png)
容量超过factor就会触发扩容,扩容为之前的两倍(list的扩容是1.5倍,map的扩容是2倍)
![](https://i-blog.csdnimg.cn/blog_migrate/1c0896691160aaeef55cefb04b248b8f.png)
||
||
\/
![](https://i-blog.csdnimg.cn/blog_migrate/85a2afb38c0fe384d6841321da47534a.png)
头插法,但是不会构成死链,因为segment是加了锁的。
每个segment数组的扩容各自扩容,互不干扰。
演示segment[0]
![](https://i-blog.csdnimg.cn/blog_migrate/479081c893587d7ee09e0eb9c794ab5b.png)
刚开始的时候,其它segment下并没有小数组,唯独segment[0]下有一个初始容量的小数组。
为什么?
![](https://i-blog.csdnimg.cn/blog_migrate/3b5d3b180f8171e6457f833ec1adf2c8.png)
因为segment[0]作为其它segment的原型。其它segment创建小数组的时候会复制segment[0]的容量和扩容因子。
所以当segment[0]扩容后,后续segment新建的小数组也会随之而扩容。
![](https://i-blog.csdnimg.cn/blog_migrate/6bb54e8ae03b7258bc18cc56c35eb339.png)
28 concurrenthashmap jdk 1.8
与7简单比较
8:数组+链表/红黑树 (去掉了segment)
7的初始化是饿汉式,而8的初始化是懒汉式
构造参数含义
下图如果初始容量是16的话,12个元素达到了扩容标准,所以初始容量为32。
![](https://i-blog.csdnimg.cn/blog_migrate/3d4885bf0c7013fe8ab14fe9c3fc2a78.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ce062c27f990399b16f6ac9ab3ccd521.png)
factor只在初始化的时候使用,后续的扩容还是按照3/4扩容
演示并发put
![](https://i-blog.csdnimg.cn/blog_migrate/ac4b6c1f7183f1e715b94ffd980c4d3e.png)
b put 下标为10的位置10s,此时c put下标为10的位置会堵塞住。
同时1.8时变为尾插法
演示扩容
![](https://i-blog.csdnimg.cn/blog_migrate/8d481988054685cb5d338afbd9da6d91.png)
扩容是按照下标从后往前进行的。处理完一个node,会把原位置替换为forwardingnode,当其它线程来访问这个位置就知道这个位置已经处理过了,不会重复处理。注意如果原节点下的链表长度大于1,那扩容后的新节点对象可能不一样,这个需要特殊处理。而原节点下的链表长度等于1的话,直接把原节点下的对象复制到新节点下。
![](https://i-blog.csdnimg.cn/blog_migrate/f6de967418eef417efa1af30bb50003a.png)
扩容细节
![](https://i-blog.csdnimg.cn/blog_migrate/51a3eef97053ae03662cf99f624fccc6.png)
如果在上图所示的这个时刻有线程进行查找操作,如果链表头是forwardingnode,则去新链表中去查找数据,否则直接获取原数据
后面这里全没听懂
33 ThreadLocal作用
![](https://i-blog.csdnimg.cn/blog_migrate/5a6f72c9eaf8490b43805de19130731f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6131a11b7f22435e9107f900a1322933.png)
![](https://i-blog.csdnimg.cn/blog_migrate/695b977fa766b9ec0c8bf8cce752c14b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9f195d6a6c6c7f4d549eab81916094d9.png)
34 ThreadLocal 原理
一个ThreadLocal对象怎么实现多个线程的Connection对象的隔离的呢?
![](https://i-blog.csdnimg.cn/blog_migrate/ad7d7456ed018e0c36789016e2a64f55.png)
起到线程隔离作用的是每个线程里的ThreadLocalMap集合
![](https://i-blog.csdnimg.cn/blog_migrate/be2b55a3fbb90c80b22970054486723f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6d1ead299f8ba593832543ae24dcfb3b.png)
新的ThreadLocal对象哈希值会改变
扩容如下 16 * 2 / 3 , 容量超过10之后会扩容
![](https://i-blog.csdnimg.cn/blog_migrate/eb924d32d058cdd05e78a8126d81ac07.png)
ThreadLocal采用开放寻址法来解决哈希冲突问题。(从哈希冲突的位置往后找下一个空闲的位置)
35 ThreadLocal_key内存释放时机
为什么ThreadLocal中的key要设置为弱引用?
![](https://i-blog.csdnimg.cn/blog_migrate/bf07d4c356dadeb3d4fdb1cad6bef87b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/57d818aaca3dfc5d13d0f4887ddde99a.png)
36 ThreadLocal_value内存释放时机_get时
![](https://i-blog.csdnimg.cn/blog_migrate/6f4147c7739eb36248309d2185919d8c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/99dd7ef8cf5afbfa9491303532c9cde1.png)
ThreadLoaclMap的特别之处,get操作如果键不存在,会把键插入
37 ThreadLocal_value内存释放时机 set时
set key时,会使用启发式扫描,清除临近的null key,启发次数与元素个数(元素越多启发范围越大),是否发现null key(发现null key启发范围越大)有关。
![](https://i-blog.csdnimg.cn/blog_migrate/42de605b3d6912cec8a92709261e200d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/aa0ff3cbdfc8f4a5ac4a26b06aa0b770.png)
不止清理8,9、10的value也会被清理掉
38 ThreadLocal_value内存释放时机 remove时
![](https://i-blog.csdnimg.cn/blog_migrate/36cb30cd3088d54b3a528510b4dc7667.png)
一般我们都用静态变量来引用ThreadLocal对象,既然静态对象和ThreadLocal对象之间是强引用。那么垃圾回收时不能把它回收掉。虽然ThreadLocal对象在Map中是弱引用,但是静态对象一直对ThreadLocal对象保持强引用,垃圾回收根本没办法回收。所以我们应该手动remove,不应该依赖gc这种被动的清理。