1、 简介
Lock是java.util.concurrent.locks包下的一个接口。
Lock接口规定了如下方法:
- lock():获取锁。如果锁不可用,那么线程调度目的,当前线程将被禁用并处于休眠状态,直到获取锁为止。
- lockInterruptibly():如果当前线程未被中断,则获取锁;
- tryLock():只有在调用时,锁是空闲的,才获取锁。如果可用,则获取锁并立即返回true,如果锁不可用,则立即返回false;
- tryLock(long TimeUnit):如果锁在给定的等待时间内空闲,且当前线程未被中断,则获取锁;
- unlock():释放锁。在等待条件前,锁必须由当前线程保持。如果调用了Condition.await(),将在等待前以原子方式释放锁,并在等待返回前,重新获取锁。
- newCOndition():返回绑定到此Lock实例的新Condition实例;
它具有如下功能/优势:
- Lock方式来获取锁支持中断、超时不获取、是非阻塞的
- 提高了语义化,哪里加锁,哪里解锁都得写出来;
- Lock显示锁可以给我们带来很好的灵活性,或者说锁的粒度更细,但是同时我们必须手动释放锁;
- 支持Condition条件对象;
- 允许多个读线程同时访问共享资源;
Lock接口下,有如下实现类:
可以看到,除了ReentrantLock外,其他实现类都是位于其他类的内部,我们使用Lock接口的显示锁,其实主要是使用ReentrantLock。
2、ReentrantLock例子及知识点
下面两个例子中,用ReentrantLock代替synchronized,用Condition.await()和Condition.signal()代替Object.wait()和Object.notify()。
package com.atguigu.sh.juc.study01;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* 【第二节】
*
* 题目:
* 两个线程,可以操作初始值为0的一个变量;
* 要求实现一个线程对该变量+1,另一个线程对该变量-1,交替进行,进行10轮
*
* ->
* 1. 高耦合第内聚的前提下,线程操作资源类
* 2. 判断/干活/通知
* 3. 多线程交互中,必须要防止多线程的虚假唤醒,也即(多线程的判断中,不可以用if,只可以用while)
* 4. -->请去本包下 ThreadOrderAccess 类下查看
*
*
* @author yuanhai
* @date 2022年04月23日
*/
// 资源类
class AirConditioner {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment () {
lock.lock();
try {
// 1. 判断
while (number != 0) {
condition.await(); // this.wait();
}
// 2.干活
number ++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
// 3.通知
condition.signalAll(); // this.notifyAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement () {
lock.lock();
try {
// 1. 判断
while (number == 0) {
condition.await(); // this.wait();
}
// 2.干活
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
// 3.通知
condition.signalAll(); // this.notifyAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// public synchronized void increment () throws InterruptedException {
// // 1. 判断
// while (number != 0) {
// this.wait(); // wait()会停下线程,同时释放锁,把控制权交出去
// }
// // 2.干活
// number ++;
// System.out.println(Thread.currentThread().getName()+"\t"+number);
// // 3.通知
// this.notifyAll();
// }
//
// public synchronized void decrement () throws InterruptedException {
// // 1. 判断
// while (number == 0) {
// this.wait(); // wait会停下线程,同时释放锁,把控制权交出去
// }
// // 2.干活
// number--;
// System.out.println(Thread.currentThread().getName()+"\t"+number);
// // 3.通知
// this.notifyAll();
// }
}
public class ThreadWaitNotifyDemo {
public static void main(String[] args) {
AirConditioner airConditioner = new AirConditioner();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(400);
airConditioner.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(500);
airConditioner.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(400);
airConditioner.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(500);
airConditioner.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}