Lock锁
- 从JDK5.0开始,Java提供了更强大的线程同步机制,通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当。
- java.util.concurrent.locks.Lock 接口时控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问。每次只能有一个线程对 Lock 对象加锁,线程开始访问共享资源前都应先获得Lock对象。
- ReentrantLock 类实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是 ReentrantLock,可以显式加锁,释放锁。
- ReentrantLock 称为
可重入锁
下面通过代码来演示一下
package org.javaboy.juc;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: bai
* @DateTime: 2020/6/7 21:02
*/
public class TestLock implements Runnable {
int ticketNums = 10;
private final ReentrantLock lock = new ReentrantLock(); // 定义lock锁
@Override
public void run() {
while (true) {
try {
lock.lock();// 显式加锁
if (ticketNums > 0) {
try {
Thread.sleep(100); // 模拟延时操作
System.out.println(Thread.currentThread().getName() + "::" + ticketNums--);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
} finally {
lock.unlock(); // 显示解锁
}
}
}
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock, "小明").start();
new Thread(testLock, "小李").start();
new Thread(testLock, "黄牛党").start();
}
}
代码解释:首先我们模拟多人抢票的场景,首先规定票数只有10张,然后我们通过票数判断是否已经被购完。在没有加入 ReentrantLock 之前,我们模拟的这个抢票场景会出现数据不一致问题,然后我们通过定义 ReentrantLock 锁,通过 lock.lock() 方法来显式加锁,通过 lock.unlock() 方法来显式解锁。这样就可以保证我们的数据一致性问题。
注意:一般我们显示加锁和解锁建议写在try/catch块中,这样就算发生了异常,finall 块依旧能够执行,我们的锁也可以被释放。这样就保证了程序的最起码的健壮性。
synchronized 与 Lock 的对比
- Lock 是显式锁(手动开启和关闭),synchronized 是隐式锁,出了作用域自动释放。
- Lock 只有代码块锁,synchronized 有代码块锁和方法锁。
- 使用 Lock 锁,JVM 将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的字类)
- 优先使用顺序:Lock > 同步代码块(已经进入了方法体,分配了响应资源)> 同步方法(在方法体之外)