这篇博文总结一些面试之中Java多线程的一些关键问题。每一个问题,会简要介绍,一些问题,下面会附上详细的介绍博文链接,方便小伙伴们更好的学习和交流。谢谢大家。
目录
CAS
Compare and Swap ,也就是比较和替换。假设有三个操作数:内存值V、旧的预期值A、要修改的值B。当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回true,否则什么都不做并返回false。
当然CAS一定要volatile变量配合,由于volatile的可见性,才能保证每次拿到的变量是主内存中最新的值。
AQS
抽象队列同步器。JUC的核心就是AQS-AbstractQueuedSynchronized。在内部,以一个双向队列维护所有的Entry。例如,在ReetrantLock中,所有等待获取锁的线程都被放在一个Entry中连接成双向队列。
参考博文: Java并发编程基础—(9)Java中的锁—队列同步器
自旋
由Synchronized修饰的代码块,在一个线程进入代码块后,其他线程必须在外等待进入阻塞状态。在很大程度上降低了程序运行的效率。如果,Synchronized修饰的代码块运行速度较快,那么可以尝试让其他等待锁的线程在Synchronized做忙循环,也就是自旋。如果做了多次忙循环,仍然没有获得锁,再进入阻塞状态。
volatile关键字
- 可见性 :对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写。
- 原子性:对单个volatile变量的读/写具体原子性,但对于Volatile++这样的操作(写+赋值,本质上是两步操作)不具有原子性。
内存语义分析:
volatile关键字能保证可见性的原因是,假设现在有线程A和线程B,线程A对volatile边;1进行修改,然后线程B读一个volatile变量时,JVM会把线程B的本地内存置为无效,接下来去共享内存中读取共享变量。
Synchronized的实现
Synchronized可以保证同步代码块在某个时间内,只有一个线程进入,其他线程在队列中阻塞,等待。那么在底层是如何实现的呢?
在底层,对于同步代码块使用了monitorenter(监视器进入)和monitorexit(监视器退出)指令。对于Synchronized修饰的同步方法,使用了ACC_SYNCHRONIZED。本质上,是对一个对象的监视器(monitor)进行获取,而且这个获取的过程是互斥的,在某一时刻只能有一个线程获取到由Synchronized保护的对象监视器。
只有获得了这个监视器才可以进入同步代码块或者同步方法,未获得的线程被阻塞在同步代码块或者同步方法的入口处,进入BLOCKED(阻塞)状态。