JUC笔记之ReentrantLock
ReentrantLock
相对于synchronized它具备如下特点
· 可中断
· 可以设置超时时间
· 可以设置为公平锁
· 支持多个条件变量(多个wait set,不同于synchronized的wait set,ReentrantLock的wait set在同一条件下notify才能唤醒WATING状态的线程)
与synchronized一样,都支持可重入
基本语法
//获取锁
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
try{
//临界区
}finally{
//释放锁
reentrantLock.unlock();
}
可重入
可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的持有者,因此有权力再次获取这把锁。如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住
代码体现
@Slf4j(topic = "c.Demo")
public class Demo {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
reentrantLock.lock();
try {
log.debug("enter main");
m1();
}finally {
reentrantLock.unlock();
}
}
public static void m1(){
reentrantLock.lock();
try {
log.debug("enter m1");
m2();
}finally {
reentrantLock.unlock();
}
}
public static void m2(){
reentrantLock.lock();
try {
log.debug("enter m2");
}finally {
reentrantLock.unlock();
}
}
}
可打断
用lockInterruptibly()来生成的锁才能打断
代码体现
@Slf4j(topic = "c.Demo2")
public class Demo2 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
//如果没有竞争那么此方法就会获取lock对象锁
//如果有竞争就进入阻塞队列,可以被其他线程用 interruput 方法打断
log.debug("尝试获得锁");
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("没有获得到锁");
return;
}
try {
log.debug("获取到锁");
}finally {
lock.unlock();
}
}, "t1");
lock.lock();
t1.start();
Thread.sleep(1000);
log.debug("打断 t1");
t1.interrupt();
}
}
锁超时
立刻失败
@Slf4j(topic = "c.Demo3")
public class Demo3 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(()->{
log.debug("尝试获得锁");
if (!lock.tryLock()) {
log.debug("获取不到锁");
return;
}
try {
log.debug("获取到锁");
}finally {
lock.unlock();
}
},"t1");
lock.lock();
log.debug("获取到锁");
t1.start();
}
}
含超时时间的
@Slf4j(topic = "c.Demo3")
public class Demo3 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
log.debug("尝试获得锁");
try {
if (!lock.tryLock(2, TimeUnit.SECONDS)) {
log.debug("获取不到锁");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("获取不到锁");
return;
}
try {
log.debug("获取到锁");
}finally {
lock.unlock();
}
},"t1");
lock.lock();
log.debug("获取到锁");
t1.start();
Thread.sleep(1000);
log.debug("释放了锁");
lock.unlock();
}
}
用ReentrantLock解决哲学家用餐问题
筷子类
class Chopstick extends ReentrantLock {
String name;
public Chopstick(String name) {
this.name = name;
}
@Override
public String toString() {
return "Chopstick{" +
"name='" + name + '\'' +
'}';
}
}
哲学家类
@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread{
Chopstick left;
Chopstick right;
public Philosopher(String name, Chopstick left, Chopstick right){
super(name);
this.left = left;
this.right = right;
}
@Override
public void run() {
while (true){
//尝试获得左手筷子
if (left.tryLock()) {
try {
//尝试获取右手筷子
if (right.tryLock()) {
try {
eat();
}finally {
right.unlock();
}
}
}finally {
left.unlock();//释放手里的左筷子
}
}
}
}
private void eat(){
log.debug("eating...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
test类
@Slf4j(topic = "c.SoleDeadLockDemo")
public class SoleDeadLockDemo {
public static void main(String[] args) {
Chopstick c1 = new Chopstick("1");
Chopstick c2 = new Chopstick("2");
Chopstick c3 = new Chopstick("3");
Chopstick c4 = new Chopstick("4");
Chopstick c5 = new Chopstick("5");
new Philosopher("哲学1",c1,c2).start();
new Philosopher("哲学2",c2,c3).start();
new Philosopher("哲学3",c3,c4).start();
new Philosopher("哲学4",c4,c5).start();
new Philosopher("哲学5",c5,c1).start();
}
}
公平锁
ReentrantLock默认是不公平的,但是可以通过构造方法改成公平的
ReentrantLock reentrantLock = new ReentrantLock(true);
公平锁一般没有必要,会降低并发度。
条件变量
synchronized中也有条件变量,就是waitSet休息室,当条件不满足时进入waitSet等待
ReentrantLock的条件变量比synchronized强大之处在于,它是支持多个条件变量的,这就好比
· synchronized是那些不满足条件的线程都在一间休息室等消息
· 而ReentrantLock支持多间休息室,唤醒时也是按休息室来唤醒
使用流程
· await前需要获取锁
· await执行后,会释放锁,进入conditionObject等待
· await的线程被唤醒(或打断、或超时)取重新竞争lock锁
· 竞争lock锁成功后,从await后继续执行
@Slf4j(topic = "c.Demo5")
public class Demo5 {
static final ReentrantLock lock = new ReentrantLock();
static private boolean hasSmoke = false;
static private boolean hasEat = false;
static Condition smoke = lock.newCondition();
static Condition eat = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
lock.lock();
try {
log.debug("有烟没");
while (!hasSmoke) {
log.debug("没烟我先休息");
try {
smoke.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有烟,那没事了");
}finally {
lock.unlock();
}
},"小男").start();
new Thread(()->{
lock.lock();
try {
log.debug("有饭没");
while (!hasEat){
log.debug("没饭我先休息了");
try {
eat.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有饭那没事了");
}finally {
lock.unlock();
}
},"小女").start();
Thread.sleep(1000);
new Thread(()->{
lock.lock();
try {
log.debug("送饭的到了");
hasEat = true;
eat.signal();
}finally {
lock.unlock();
}
},"送饭的").start();
Thread.sleep(1000);
new Thread(()->{
lock.lock();
try {
log.debug("送烟的到了");
hasSmoke = true;
smoke.signal();
}finally {
lock.unlock();
}
},"送烟的").start();
}
}