可重入锁
可重入锁,指的是同一线程 外层方法获得锁
之后 调用了仍然需要获取同一个锁
的方法,不用重新获取锁
,可以直接执行。在JAVA中synchronized和ReentrantLock 都是 可重入锁。
可重入锁最大的作用是避免死锁
代码示例1:
public class MyRunnable implements Runnable{
public synchronized void fun1(){
System.out.println(Thread.currentThread().getName());
fun2();
}
public synchronized void fun2(){
System.out.println(Thread.currentThread().getName());
}
@Override
public void run() {
fun1();
}
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
new Thread(mr).start();
new Thread(mr).start();
new Thread(mr).start();
}
}
代码示例2:
ublic class MyRunnable implements Runnable {
ReentrantLock lock = new ReentrantLock();
public void fun1() {
lock.lock();
System.out.println(Thread.currentThread().getName());
fun2();
lock.unlock();
}
public void fun2() {
lock.lock();
System.out.println(Thread.currentThread().getName());
lock.unlock();
}
@Override
public void run() {
fun1();
}
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
new Thread(mr).start();
new Thread(mr).start();
new Thread(mr).start();
}
}
可重入自旋锁
在获取不到锁的情况下,会让当前线程一直做空循环,避免进入阻塞状态
public class SpinLock {
private AtomicReference<Thread> owner =new AtomicReference<>();
private int count =0;
// 获取锁
public void lock(){
Thread current = Thread.currentThread();
// 为了避免不可重入,在lock的时候需要判断操作的是否是同一线程
// 同一线程,多次调用lock,计数 +
if(current==owner.get()) {
count++;
return ;
}
// CAS不成功,空循环
while(!owner.compareAndSet(null, current)){
}
// 执行到这里,就表示获取到锁了
}
// 释放锁
public void unlock (){
Thread current = Thread.currentThread();
if(current==owner.get()){
// 同一线程,多次调用unlock,计数归零后,才能将锁释放掉
if(count!=0){
count--;
}else{
owner.compareAndSet(current, null);
}
}
}
}
可中断锁
如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,线程B不想等待,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。
在Java中,synchronized不是可中断锁,而Lock是可中断锁。
Lock#lockInterruptibly()
与Lock#lock()
方法(待获取锁成功后,才响应中断。)不同,
优先考虑响应中断,再去获取锁。
/**
* 获取锁,除非线程被打断。
* 如果锁不被其他线程持有,则获取锁,并立即返回,将锁持有计数设置为1。
* 如果当前线程已经持有此锁,那么持有计数将增加1,方法立即返回。
* 如果锁被另一个线程持有,那么当前线程将出于线程调度的目的被禁用,
* 并处于休眠状态,直到发生以下两种情况之一:
* 1.锁由当前线程获取。
* 2.其他线程打断当前线程。
* 如果锁被当前线程获取,那么锁持有计数被设置为1。
*
* 如果当前线程:进入此方法时,其中断状态是否已设置
* 或者当获取锁时被打断
* 则会抛出InterruptedException,并清除interrupted status。
*
* 在这个实现中,由于这个方法是一个显式的中断点,
* 所以优先响应中断而不是正常的或可重入的锁获取。
*
* @throws InterruptedException if the current thread is interrupted
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
* 以独占模式获取,如果中断将中止。
* 首先检查中断状态,然后至少调用一次{@link #tryAcquire},成功后返回。
* 否则,线程将排队,可能反复阻塞和解除阻塞,调用{@link #tryAcquire},直到成功或线程被中断。
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
公平锁
尽量按照请求顺序来获取锁。比如同时有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。
/**
* 如果参数为true表示为公平锁,为fasle为非公平锁。
* 默认情况下,如果使用无参构造器,则是非公平锁。
**/
ReentrantLock lock = new ReentrantLock(true);
读写锁
对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。可以通过readLock()获取读锁,通过writeLock()获取写锁。