-
Java中锁的分类
-
可重入锁、不可重入锁
可重入锁:synchronized、reentrantLock、reentrantReadWriteLock
不可重入锁:Worker
-
乐观锁、悲观锁
悲观锁:synchronized、reentrantLock、reentrantReadWriteLock
乐观锁:CAS
-
公平锁、非公平锁
Synchronized只能是非公平锁
ReentrantLock、reentrantReadWriteLock可以实现公平锁和非公平锁。
-
互斥锁、共享锁
Synchronized、reentrantLock是互斥锁
ReentrantReadWriteLock可以实现互斥锁和共享锁
-
Synchronized的优化
锁消除:不存在操作临界资源时锁被优化掉
锁膨胀:频繁进行锁获取和释放,会将锁扩大范围
锁升级:synchronized在jdk1.6之前,如果获取不到锁,就会挂起当前线程。
Jdk1.6做了锁升级优化:
无锁、匿名偏向:当前对象没有作为锁存在
偏向锁:如果当前锁资源只存在一个线程在频繁获取和释放,那么这个线程来只需要判断线程是否是当前线程。如果是,直接拿走锁资源;如果不是,基于CAS尝试将偏向锁指向当前进程。如果获取不到,触发锁升级,锁升级为轻量级锁。(也就是说,偏向锁出现竞争就触发锁升级)
轻量级锁:频繁的CAS尝试(自适应自旋锁),如果成功获取,拿到锁资源。自旋到一定次数还没有获取到锁资源,触发锁升级。
重量级锁
-
Synchronized的实现原理
Synchronized同步语句块的实现使用的是monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。
执行monitorenter流程:
对象锁的拥有者线程才可以执行monitorexit指令释放锁,monitorexit执行流程:
-
Synchronized和lock的区别
Synchronized和lock都是java中用来解决线程安全的工具。
特性:synchronized是java中的同步关键字;lock是juc包提供的一个接口,这个接口有很多实现类,比如reentrantlock。
锁的粒度:Synchronized可以通过修饰在方法上或修饰在代码块上来控制锁的粒度。还可以通过加锁对象的生命周期来控制锁的作用范围,如锁对象是类对象,那么锁的全局锁,如果是实例对象,则锁范围取决于实例的生命周期;Lock的锁粒度通过它提供的lock()和unlock()方法实现。
灵活性:synchronized简单易用,但功能基础。它是不可中断锁;lock提供了更高级的功能,如可中断锁(lockInterruptibly())、尝试非阻塞地获取锁(tryLock())、尝试在给定最大时间内获取锁(tryLock(long time, TimeUnit unit)),以及支持公平锁等。
条件变量支持:synchronized:需要配合Object类的wait()、notify()和notifyAll()方法实现等待/通知机制;Lock:提供了Condition接口来实现分组唤醒需要唤醒的线程,可以更精细地控制多线程的等待与通知。
名词解释:
不可中断:如果一个线程在等待获取synchronized锁时被阻塞,它不能被中断,即不能响应中断请求。这意味着,该线程必须等到它之前的线程释放锁之后才能继续执行,这在某些情况下可能会导致问题(你想让它因为某些原因(如超时、任务取消等)放弃等待,也做不到,它会一直等下去,直到获取到锁),比如死锁或长时间等待。
可中断:Lock接口提供了lockInterruptibly()方法,允许线程在等待锁的过程中响应中断。这意味着,如果一个线程A在等待获取锁时被中断,它可以退出等待锁的状态,进而处理中断请求,这为处理长时间等待或死锁提供了一种可能的解决方案。
-
synchronized和reentrantLock的区别(reentrantLock是lock的一个实现类,所以这个问题的回答与上题基本一致)
synchronized是java关键字,提供了一种隐式的锁管理机制,锁一个对象,当一个线程进入synchronized方法或代码块时,自动获取锁,退出时自动释放锁。ReentrantLock是juc包下的一个类,提供了一种显示锁管理机制,需要手动获取和释放锁。
synchronized 比较简单,功能相对有限,主要用于实现方法或代码块的同步。
ReentrantLock 提供了比synchronized更丰富的功能,例如尝试非阻塞地获取锁(tryLock())、可中断地获取锁(lockInterruptibly())、支持公平锁等。
条件变量支持:
synchronized 与Object类中的wait()、notify()和notifyAll()方法结合使用,来实现等待/通知机制。
ReentrantLock 使用Condition接口来提供等待/通知机制,每个ReentrantLock可以关联一个或多个Condition对象,这提供了分组唤醒等待线程的能力,比synchronized的等待/通知机制更加灵活和强大。
-
什么是AQS
AbstractQueuedSynchronizer抽象类,AQS是juc包下的一个基类,juc下的很多内容都是基于AQS实现了部分功能,如ReentrantLock、TreadPoolExecutor、阻塞队列、CountDownLatch、Semaphore等。
AQS的核心思想:如果被请求的资源空闲,则将请求该资源的线程设为工作线程,并将该资源设定为锁定状态。如果被请求的资源被占用,则需要一套线程阻塞和唤醒的锁分配机制。AQS中这个机制是基于CLH锁实现的。
AQS原理:
首先,AQS提供了一个由volatile修饰,并采用CAS方式修改的int型state变量。
其次,AQS维护了一个双向链表,有head,有tail,并且每个节点都是Node对象,并维护双向链表插入操作。暂时获取不到锁的线程被封装成一个Node,在双向链表(CLH队列)中排队。
-
AQS如何添加结点和唤醒结点?
节点是从链表的尾部加入的,这个过程主要是通过enq方法实现的,确保线程安全性是通过CAS操作完成的。而唤醒操作则是针对位于头部后的节点进行的。因为头节点通常是已经执行完毕或正在执行的节点。这种设计允许AQS以公平和高效的方式管理等待的线程,确保每个线程都能有序地获取同步状态。