八股--juc

01线程状态_java中的线程状态

NEW:刚刚创建(new)出的线程对象,仅仅是个java对象,没有跟操作系统底层关联,不会被操作系统分配给cpu执行。

RUNNABLE:调用start后,只有这个状态的线程代码才会得到cpu的执行。

TERMINATED:代码执行完毕,此时操作系统底层的线程和相关资源才会得到释放。

BLOCKED:获取锁失败时进入堵塞状态,获取锁成功后回到RUNNABLE状态。

WAITING:获得了锁但是代码执行的条件还不满足,所以调用wait方法释放锁。将来条件满足时另一个线程调用notify唤醒等待的线程。

TIMED_WAITING:有时限的版本。

02线程状态_java中的线程状态代码演示

03 五种状态vs六种状态

新建、就绪、运行、堵塞、终结

04 线程池核心参数_简介

核心线程满了放到堵塞队列。

堵塞队列满了,此时救急线程出场(keepAliveTime + unit 时间内救急线程没有任务,救急线程就会从线程池中去掉)。

救急线程 也到上限了,此时就要用到拒绝策略。

05 线程池核心参数演示

救急线程执行完后发现堵塞队列中还有任务,就会把堵塞队列中的任务也执行掉

AbortPolicy()拒绝策略:直接抛异常

CallerRunsPolicy()拒绝策略:由主线程运行

DiscardPolicy():直接把这个任务丢弃

DiscardOldestPolicy():把等待最久的任务从任务队列中丢弃,把新的任务放入队列。

06 wait vs sleep

打断唤醒:拿到线程对象的话可以使用interrupt方法打断

07 wait sleep演示

08 lock vs synchronized

Lock对象可以知道哪些线程在这个锁对象上等待。公平锁的效率不如非公平锁。

09 lock_堵塞演示

还未加锁时的Lock

t1、t2、t3、t4线程依次来获取Lock

t1获得Lock时

t1释放锁后

t2释放锁后

不是默认非公平锁吗,怎么执行顺序还是跟获取锁的顺序相同,那这个非公平体现在哪?

10 lock_公平非公平演示

因为上面是所有线程都在堵塞队列中的情况

11 lock_条件变量演示

await后到等待队列中等待

signAll后唤醒的线程到堵塞队列尾部

12 volatile 举例

13 volatile 原子性 演示

14 volatile 可见性 演示

主线程并没有停下来

15 volatile 可见性 原因与解决

发现stop从内存中读了几十万次都是false,jit会把这个stop变量直接替换为false

对变量加上volatile后jit就不会对这个变量进行优化。

16 volatile 有序性 分析

17

18 volatile 有序性 volatile位置不同分析

volatile修饰的变量在写时会加一个向上的写屏障,防止上面的代码跑下来

volatile修饰的变量在读时会加一个向下的读屏障,防止下面的代码跑上来

但是如果volatile加在x上,达不到防止指令重排序的效果

19 悲观锁、乐观锁

20 悲观锁_乐观锁_unsafe_cas

cas的对象必须加volatile,因为cas能保证原子性,但是我们还要保证所操作的对象是可见的。

21 悲观锁_乐观锁_代码对比

悲观锁

乐观锁

22 Hashtable vs concurrenthashmap

23 hashtable演示

不需要二次哈希

24 concurrenthashmap jdk 1.7

segment + 数组(Map.Entry结构) + 链表

演示并发度

clevel:并发度,即有几个segment

capacity:capacity / clevel 即一个segment下的数组有多大,如果capacity<clevel(segment下的数组长度最小为2)

演示索引计算

segment下标的计算:二次哈希后得到的值对应的二进制数取前n位(2^n=clevel)对应的十进制值。

小数组下标的计算,低n位对应的十进制值,(2^n = min(2, capacity / clevel)

演示扩容

segment的容量是不能扩容的,在构造函数中确定了并发度后就一直不会变。

容量超过factor就会触发扩容,扩容为之前的两倍(list的扩容是1.5倍,map的扩容是2倍)

||

||

\/

头插法,但是不会构成死链,因为segment是加了锁的。

每个segment数组的扩容各自扩容,互不干扰。

演示segment[0]

刚开始的时候,其它segment下并没有小数组,唯独segment[0]下有一个初始容量的小数组。

为什么?

因为segment[0]作为其它segment的原型。其它segment创建小数组的时候会复制segment[0]的容量和扩容因子。

所以当segment[0]扩容后,后续segment新建的小数组也会随之而扩容。

28 concurrenthashmap jdk 1.8

与7简单比较

8:数组+链表/红黑树 (去掉了segment)

7的初始化是饿汉式,而8的初始化是懒汉式

构造参数含义

下图如果初始容量是16的话,12个元素达到了扩容标准,所以初始容量为32。

factor只在初始化的时候使用,后续的扩容还是按照3/4扩容

演示并发put

b put 下标为10的位置10s,此时c put下标为10的位置会堵塞住。

同时1.8时变为尾插法

演示扩容

扩容是按照下标从后往前进行的。处理完一个node,会把原位置替换为forwardingnode,当其它线程来访问这个位置就知道这个位置已经处理过了,不会重复处理。注意如果原节点下的链表长度大于1,那扩容后的新节点对象可能不一样,这个需要特殊处理。而原节点下的链表长度等于1的话,直接把原节点下的对象复制到新节点下。

扩容细节

如果在上图所示的这个时刻有线程进行查找操作,如果链表头是forwardingnode,则去新链表中去查找数据,否则直接获取原数据

后面这里全没听懂

33 ThreadLocal作用

34 ThreadLocal 原理

一个ThreadLocal对象怎么实现多个线程的Connection对象的隔离的呢?

起到线程隔离作用的是每个线程里的ThreadLocalMap集合

新的ThreadLocal对象哈希值会改变

扩容如下 16 * 2 / 3 , 容量超过10之后会扩容

ThreadLocal采用开放寻址法来解决哈希冲突问题。(从哈希冲突的位置往后找下一个空闲的位置)

35 ThreadLocal_key内存释放时机

为什么ThreadLocal中的key要设置为弱引用?

36 ThreadLocal_value内存释放时机_get时

ThreadLoaclMap的特别之处,get操作如果键不存在,会把键插入

37 ThreadLocal_value内存释放时机 set时

set key时,会使用启发式扫描,清除临近的null key,启发次数与元素个数(元素越多启发范围越大),是否发现null key(发现null key启发范围越大)有关。

不止清理8,9、10的value也会被清理掉

38 ThreadLocal_value内存释放时机 remove时

一般我们都用静态变量来引用ThreadLocal对象,既然静态对象和ThreadLocal对象之间是强引用。那么垃圾回收时不能把它回收掉。虽然ThreadLocal对象在Map中是弱引用,但是静态对象一直对ThreadLocal对象保持强引用,垃圾回收根本没办法回收。所以我们应该手动remove,不应该依赖gc这种被动的清理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值