目录页:https://blog.csdn.net/u011294519/article/details/88367808
1. ReentrantLock
1.1.小声哔哔
年轻的时候,说到锁想到的就是synchronized关键字,而且那时候项目要求不高,已经完全能满足需求,但是深入了解synchronized以后发现自己真的是年轻(貌似说synchronized已经被优化的与Lock差不多,但是真的了解过后发现由于synchronized本身的限制导致在读写锁方面是synchronized永远也赶不上的)。
ReentrantLock从命名上来看就是可重入,但是synchronized其实也是可重入的
ReentrantLock与synchronized的区别:
- synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的。
- 如果一个方法被Synchronized修饰作为对象锁,甚至是类锁(本质也是对象锁),那么一个线程获取到锁后其他线程就被阻塞,直到该线程释放锁。ReentrantLock支持设置等待时间,并且可阻断。
- 在释放锁的方式上,synchronized在开发过程中完全可以不用去关心,在方法执行完成或执行异常后,JVM会将锁释放,这也是内置关键字的好处。而ReentrantLock则需要我们显示的释放锁,而且必须是要写在finally中防止异常导致死锁。由此可以看出在使用方式上ReenTrantLock比synchronized更加灵活,但是synchronized使用起来更加简单。
1.2. ReentrantLock的一些特性
- ReenTrantLock可以通过构造方法指定使用公平锁还是非公平锁。Synchronized关键字只能是非公平锁。(PS:公平锁就是FIFO这种,但是非公平锁效率会更高,因为公平锁要加判断等进行实现,有这功夫活都干完了)
- ReenTrantLock利用AQS提供了一个Condition(条件)类,可以对线程分组唤醒,而synchronized则是会唤醒全部线程来竞争锁。
- ReenTrantLock可以使用tryLock尝试获取锁,并不是一直阻塞。
1.3.ReentrantLock主要方法
构造方法ReentrantLock():初始化非公平锁
构造方法public ReentrantLock(boolean fair):传入true初始化公平锁,若传入参数false则初始化非公平锁。
lock():获取锁
unlock():释放锁
boolean tryLock():尝试获取锁,若获取成功则返回true
boolean tryLock (long timeout, TimeUnit unit):尝试获取锁,若此时无法获取锁在等待timeout时间后放弃获取锁并返回false。
lockInterruptibly():以响应中断的方式加锁
1.4. 上代码
1.4.1. 实现与synchronized相同效果
先使用ReentrantLock达到和synchronized相同的效果,使用递归体现可重入
package com.concurrent.aqslock.part8.reent;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用显示锁的范式
*/
public class LockDemo {
private static ReentrantLock lock = new ReentrantLock();
/**
* 使用ReentrantLock锁
*/
static class ReenTrantThread implements Runnable {
private int count = 0;
@Override
public void run() {
cyclic();
}
private void cyclic() {
lock.lock();
try {
while (count < 2) {
System.out.println(Thread.currentThread().getName() + " count value: " + count);
Thread.sleep(1000);
++count;
System.out.printf("Lock is get by CurrentThread: %s %s \n", Thread.currentThread().getName(), lock.isHeldByCurrentThread());
cyclic();
Thread.sleep(3000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (count >= 2 && lock.isHeldByCurrentThread()) {
System.out.printf("%s unlock ,queue length: %s \n", Thread.currentThread().getName(), lock.getQueueLength());
lock.unlock();
}
}
}
}
/**
* 使用synchronized锁
*/
static class SysThread implements Runnable {
private int count = 0;
@Override
public void run() {
cyclic();
}
private synchronized void cyclic() {
while (count < 2) {
System.out.println(Thread.currentThread().getName() + " count value: " + count);
++count;
cyclic();
}
}
}
public static void main(String[] arg0) throws InterruptedException {
for (int i = 0; i < 2; ++i) {
new Thread(new ReenTrantThread(), "ReenTrantThread " + i).start();
}
for (int i = 0; i < 2; ++i) {
new Thread(new SysThread(), "SysThread " + i).start();
}
Thread.sleep(3000);
}
}
运行结果:
代码位置:aqs-lock的part8
1.4.2. 使用可中断式获取锁
package com.concurrent.aqslock.part8.reent;
import java.util.concurrent.locks.ReentrantLock;
/**
* 演示可中断式获取锁
*/
public class InterruptReenTrant {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] arg0) {
Thread t1 = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " before get lock");
lock.lockInterruptibly();
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " after get lock");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println(Thread.currentThread().getName() + " unlock");
lock.unlock();
}
}
}, "first thread");
Thread t2 = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " before get lock");
lock.lockInterruptibly();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " after get lock");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println(Thread.currentThread().getName() + " unlock");
lock.unlock();
}
}
}, "second thread");
t1.start();
t2.start();
// 尝试注释和不注释下面代码
t1.interrupt();
}
}
运行结果:
代码位置:aqs-lock的part8
1.4.3. tryLock
package com.concurrent.aqslock.part8.reent;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* 演示tryLock
*/
public class TryLockReenTrant {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] arg0) {
Thread t1 = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " before try lock");
if (lock.tryLock()) {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " after try lock");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println(Thread.currentThread().getName() + " unlock");
lock.unlock();
}
}
}, "first thread");
Thread t2 = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " before try lock");
if (lock.tryLock()) {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " after try lock");
} else {
System.out.println(Thread.currentThread().getName() + " try lock fail");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println(Thread.currentThread().getName() + " unlock");
lock.unlock();
}
}
}, "second thread");
Thread t3 = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " before try lock");
if (lock.tryLock(6L, TimeUnit.SECONDS)) {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " after try lock");
} else {
System.out.println(Thread.currentThread().getName() + " try lock fail");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println(Thread.currentThread().getName() + " unlock");
lock.unlock();
}
}
}, "third thread");
t1.start();
t2.start();
t3.start();
}
}
运行结果:
代码位置:aqs-lock的part8
1.5. 看代码
非公平队列的Lock():
可以看到,使用了我们的老朋友AQS的compareAndSetState方法,若state为0时则置为1,设置锁的拥有者为当前线程。
若更新state失败,则进入acquire方法
若获取到锁的为当前现在,则给state加1,并直接返回
若获取到锁的不是当前线程,则进入acquireQueued方法
首先将当前线程加入等待队列,并挂起当前线程。