可中断锁与不可中断锁
在Java中有两种锁,一种是内置锁synchronized,一种是显示锁Lock,其中Lock 锁是可中断锁,而 synchronized 则为不可中断锁。所谓的中断锁指的是锁在执行时可被中断,也就是在执行时可以接收interrupt的通知,从而中断锁执行,不可中断锁的问题是,当出现“异常”时,只能一直阻塞等待,别无其他办法,中断锁的出现,就可以打破这一僵局,它可以在等待一定时间之后,主动的中断线程,以解决线程阻塞等待的问题;
可中断锁是指抢占过程可以被中断的锁,JUC的显式锁(如ReentrantLock)是一个可中断锁。不可中断锁是指抢占过程不可以被中断的锁,如Java的synchronized内置锁就是一个不可中断锁;锁的可中断抢占:
在JUC的显式锁Lock接口中,有以下两个方法可以用于可中断抢占:
(1)lockInterruptibly():可中断抢占锁抢占过程中会处理Thread.interrupt()中断信号,如果线程被中断,就会终止抢占并抛出InterruptedException异常;
(2)tryLock(long timeout,TimeUnit unit):阻塞式“限时抢占”(在timeout时间内)锁抢占过程中会处理Thread.interrupt()中断信号,如果线程被中断,就会终止抢占并抛出InterruptedException异常;
公平锁与非公平锁
非公平锁是指多个线程获取锁的顺序并不一定是其申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,抢锁成功的次序不一定体现为FIFO(先进先出)顺序。非公平锁的优点在于吞吐量比公平锁大,它的缺点是有可能会导致线程优先级空转或者线程饥饿现象,例如ReentrantLock锁的默认情况;
公平锁是指多个线程按照申请锁的顺序来获取锁,抢锁成功的次序体现为FIFO(先进先出)顺序,虽然ReentrantLock锁默认是非公平锁,但可以通过构造器指定该锁为公平锁;
可/不可重入的自旋锁
不可重入的自旋锁
自旋锁的基本含义为:当一个线程在获取锁的如果锁已经被其他线程获取,调用者就一直在那里循环检查该锁是否已经被释放,一直到获取到锁才会退出循环。
CAS自旋锁的实现原理为:抢锁线程不断进行CAS自旋操作去更新锁的owner(拥有者),如果更新成功,就表明已经抢锁成功,退出抢锁方法。如果锁已经被其他线程获取(也就是owner为其他线程),调用者就一直在那里循环进行owner的CAS更新操作,一直到成功才会退出循环。
代码语言:javascript复制public class SpinLock implements Lock{
/**当前锁的拥有者
* 使用Thread 作为同步状态
*/
private AtomicReference<Thread> owner = new AtomicReference<>();
/**
* 抢占锁
*/
@Override
public void lock(){
Thread t = Thread.currentThread();
//自旋
while (owner.compareAndSet(null, t)){
// DO nothing
Thread.yield();//让出当前剩余的CPU时间片
}
}
/**
* 释放锁
*/
@Override
public void unlock(){
Thread t = Thread.currentThread();
//只有拥有者才能释放锁
if (t == owner.get()){
// 设置拥有者为空,这里不需要 compareAndSet操作
// 因为已经通过owner做过线程检查
owner.set(null);
}
}
// 省略其他代码
}
可重入的自旋锁:引入一个计数器,用来记录一个线程获取锁的次数。
代码语言:javascript复制public class ReentrantSpinLock implements Lock{
/**当前锁的拥有者
* 使用拥有者 Thread 作为同步状态,而不是使用一个简单的整数作为同步状态
*/
private AtomicReference<Thread> owner = new AtomicReference<>();
/**
* 记录一个线程重复获取锁的次数
* 此变量为同一个线程在操作,没有必要加上volatile保障可见性和有序性
*/
private int count = 0;
/**
* 抢占锁
*/
@Override
public void lock(){
Thread t = Thread.currentThread();
// 如果是重入,增加重入次数后返回
if (t == owner.get()){
count;
return;
}
//自旋
while (owner.compareAndSet(null, t)){
// DO nothing
Thread.yield();//让出当前剩余的CPU时间片
}
}
/**
* 释放锁
*/
@Override
public void unlock(){
Thread t = Thread.currentThread();
//只有拥有者才能释放锁
if (t == owner.get()){
if (count > 0){
// 如果重入的次数大于0, 减少重入次数后返回
--count;
} else {
// 设置拥有者为空
// 这里不需要 compareAndSet, 因为已经通过owner做过线程检查
owner.set(null);
}
}
}
// 省略其他代码
}