JUC-复习

JUC大全----一般大公司才会问,建议把aqs学明白

线程池的原理,为什么要创建线程池?

创建线程,4种

1.new Thread(new runnable)

2.继承Thread new Thread(){run()}重写run方法

3.创建一个futureTask对象,里面重写call方法,再放入Thread里面

4.线程池

创建线程池的4种方式

newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

newFixedThreadPool 创建一个指定工作线程数量的线程池

newSingleThreadExecutor 创建一个单线程化的Executor

newScheduledThreadPool 创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行

线程5种状态

新建,就绪(调用了start之后),运行(获得了时间片之后),阻塞,死亡

Callable和Runnable区别

前者能够拿到返回值,后者没有。这两种线程池都可以submit方法调用。对于抛出的异常,call方法上有throws Exception,外层需要try-catch异常,如果不get,内层会吃掉异常。而runnable只能try-catch。

ThreadLocal结构,为什么弱引用

每个线程都有一个ThreadLocalMap。ThreadLocalMap的key是ThreadLocal,value是我们要存储的值。https://www.cnblogs.com/-beyond/p/13125195.html

volatile怎么保证可见性

mesi缓存一致性协议,当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。发现数据无效是嗅探机制,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。嗅探也会引起总线风暴,因为不断循环嗅探,所以不能大量使用volatile。https://zhuanlan.zhihu.com/p/137193948

synchronized怎么保证可见性

synchronized在修改了本地内存中的变量后,解锁前会将本地内存修改的内容刷新到主内存中,确保了共享变量的值是最新的,也就保证了可见性。先清空工作内存→在主内存中拷贝最新变量的副本到工作内存→执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁。

1)线程解锁前,必须把共享变量的最新值刷新到主内存中

2)线程加锁时,将清空工作内存***享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值(注意:加锁与解锁需要是同一把锁)

volatile和synchronized的区别

https://blog.csdn.net/suifeng3051/article/details/52611233

synchronized和ReentrantLock的区别

ReentrantLock的特点,synchronized只能是非公平锁,ReentrantLock可以指定;ReentrantLock有condition,condition可以唤醒指定线程;lock.interrupt方法可以等待线程中断

公平锁和非公平锁

https://www.jianshu.com/p/f584799f1c77

synchronized和lock在发生异常时的区别

synchronized会自动释放锁,lock不会自动释放锁,需要在finally释放锁

synchronized底层原理

sycnchroized底层存在一个monitor,每个被加锁的对象都会有一个,字节码获取锁时执行monitorenter,如果计数器为0说明可以成功获取锁,计数器现在是1,否则就阻塞。

synchronized与lock,效率比较

在多线程环境并存在大量竞争的情况下,synchronized的用时迅速上升,而lock却依然保存不变或增加很少

http://freewind.in/posts/542-which-has-better-performance-synchronized-and-lock/

操作系统的阻塞,加锁怎么实现的

https://blog.csdn.net/weixin_44367006/article/details/101637239
阻塞:进程是有时间片调度算法的,一个进程阻塞了,会放入等待队列里面,此时调度算法就不会再调度他。等他收到信号或者等待时间过了之后,再放入执行队列里面。
加锁:linux底层有futex函数可以放入操作系统的等待队列,同时告诉操作系统解锁的条件。具体到底层,还是cpu指令比如xchg可以保证原子性的比较,总线mesi协议可以保证各个cpu的值都是最新的。

unsafe的cas在汇编的实现

https://phantomvk.github.io/2018/02/08/CAS/
通过lock锁住总线,让其他cpu不能访问内存,和volatile的一样

unsafe.park和unpark

unpark是发放许可,park调用时会检查是否有许可,如果没有就阻塞

阻塞队列原理,记住源码

以arrayblockingqueue举例:https://segmentfault.com/a/1190000016311925,内部用ReentrantLock和condition实现的,notEmpty和notFull两个condition
put时候的阻塞:lock.lock()再操作,如果队列满了会调用notEmpty.await(),这里还用了while循环防止假唤醒,await这里就是aqs的等待队列了,如果没满直接入队,然后释放锁。在入队方法最后一行,需要调用notEmpty.signal,通知阻塞等待的队列过来放元素。
take删除时候的阻塞:删除的时候,先上锁,然后判断如果队列为空,也需要等待,即notEmpty.wait(),也是while循环防止假唤醒。在出队方法最后一行,需要notFull.signal,通知取元素。

AQS-AbstractQueuedSynchronizer

https://segmentfault.com/a/1190000015804888
加锁:把线程包装成节点加入队列。其他线程加锁时会cas插入到队列尾部,并Locksupport.park。a,b,c三个竞争锁,A先获取,此时没有竞争,直接获取,不进入队列;b获取时发现竞争,需要进入队列,aqs队列的特点是前一个节点表示后一个节点的状态,所以有一个伪头节点,b放入伪头节点之后,并修改伪头节点的标志位为-1,表示后面节点被阻塞。c同理。
所以现在队列是-1=>-1=>0
解锁:unlock的时候,会调用release唤醒首节点。此时需要设置首节点状态为0,表示下一个节点将要被唤醒。然后把后继节点找出来,准备unpark节点。对于节点的状态还有一种情况是超时或者取消,也就是status=1的状态,此时需要从后向前查找第一个不是被取消的节点(为什么从后往前?考虑并发入队,首先node的next和prev指针都是volatile的,具体没想明白)
此时B节点被唤醒了,需要设置头节点是自己。
现在是0=>-1=>0
无论哪种状态,B都会被唤醒,然后把自己变成头节点(如果是取消节点,会在此时进行遍历,修改队列结构)。然后c唤醒,释放锁,此时后面没有节点了,所以只剩下一个伪头节点,是当前节点。

AQS的condition

https://segmentfault.com/a/1190000015807209
//ThreadA先调用lock方法获取到锁,然后调用con.await()
//ThreadB获取锁,调用con.signal()唤醒ThreadA
//ThreadB释放锁
condtion复用了aqs的node节点类型,condition的队列我们称作条件队列,原来的队列称作等待队列。a先获取到锁,然后await。await释放锁,然后把线程包装成节点入条件队列。
b获取锁,然后signal。signal会删除条件队列的头结点,并添加到等待队列,此时等待队列是-1=>0,在释放锁之后,这个等待队列的节点会被唤醒。
b释放锁,等待队列的a会被唤醒。

CountdownLatch

await都会加入到等待队列里面。在countdown时,如果计数器为0,会唤醒(unpark)等待队列的头结点。并且会修改当前节点的状态为0,再调用propagate,向后传播状态,来唤醒队列里面的所有节点。

ConcurrentHashMap

Java多线程进阶(二三)—— J.U.C之collections框架:ConcurrentHashMap(1) 原理:https://segmentfault.com/a/1190000016096542
put会判断三种情况,get不会加锁,对于链表,直接遍历,红黑树,遇到写修改时,会直接利用list的next,当做链表查找。
Java多线程进阶(二四)—— J.U.C之collections框架:ConcurrentHashMap(2) 扩容:https://segmentfault.com/a/1190000016124883

Java多线程

  • 线程的生命周期,什么时候会出现僵死进程;
  • 说说线程安全问题,什么实现线程安全,如何实现线程安全;
  • 创建线程池有哪几个核心参数? 如何合理配置线程池的大小?
  • volatile、ThreadLocal的使用场景和原理;
  • ThreadLocal什么时候会出现OOM的情况?为什么?
  • synchronized、volatile区别、synchronized锁粒度、模拟死锁场景、原子性与可见性;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值