Lock 简介
Lock锁相对于synchronized关键字,它更加灵活(允许在不同的作用域中获取和释放锁,并允许以任何顺序获取和释放多个锁),并支持多个关联的Condition对象。
有些锁允许并发访问共享资源,例如读写锁的读锁。
随着灵活性的提高,我们需要承担更多的责任。Lock务必要通过unlock() 方法在finally块中执行,以确保在必要时释放锁。
Lock 与synchronized在锁的处理上的重要差别
- 锁的获取方式:
Lock是通过程序代码的方式由开发者 手工获取lock()、tryLock()…
Synchronized是通过JVM获取(无需开发者干预)。 - 具体实现方式:
Lock是通过程序代码的方式实现。
Synchronized是通过JVM获取(无需开发者干预)。 - 锁的释放方式:
Lock务必要通过unlock() 方法在finally块中手工释放。
synchronized是通过JVM释放(无需开发者干预)。
–
synchronized获取多个锁后(lock_1, lock_2),释放要以相反的顺序释放( lock_2, lock_1)。
Lock锁的释放,没有这种要求。 - 锁的具体类型:
Lock提供了公平锁、非公平锁。
Synchronized与 Lock均提供了 可重入锁。
结构图
方法
void lock()
;
- 获取锁。如果锁不可用,则当前线程将处于休眠状态,直到获得锁为止。
void lockInterruptibly()
throws InterruptedException;
- 除非当前线程中断,否则获取锁。
- 如果可用,则获取锁并立即返回。
- 如果锁不可用,则当前线程将处于休眠状态,直到发生以下两种情况之一:
- 1、当前线程获取锁。
- 2、其他线程中断当前线程,支持中断锁采集。
boolean tryLock()
;
- 尝试非阻塞的获取锁。仅当在调用时锁是空闲的才获取锁。
- 获取锁(如果可用),并立即返回值true。如果锁不可用,则此方法将立即返回值false。
- 此方法的典型用法是:
-
Lock lock = ...; if (lock.tryLock()) { try { // 操纵受保护状态 } finally { lock.unlock(); } } else { // 执行替代操作 }
boolean tryLock(long time, TimeUnit unit)
throws InterruptedException;
- 如果锁在给定的等待时间内空闲且当前线程没有中断,则获取该锁。
- 如果时间小于或等于零,则该方法根本不会等待。
- 如果锁可用,此方法立即返回值true。如果锁不可用,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
- 1、当前线程在超时时间内获得锁
- 2.、当前线程在超时时间内被中断
- 3、 超时时间结束,返回false
- 如果当前线程,在获取锁时被中断,会抛出异常并清除当前线程的中断状态。
voidunlock()
;
- 释放锁
Condition newCondition()
;
- 返回绑定到此锁实例上的一个新Condition实例。
案例:演示可重入性
class MyLock {
// 可重入锁, 表示同一个线程可以重复获取到刚获取过的锁。
private Lock lock = new ReentrantLock();
public void myThread1() {
lock.lock();
try {
// 这个方法会被执行10次,因为lock是可重入锁,在线程t2拿不到锁的情况下,
// 线程t1虽然没有释放锁,但却可以重复执行。
System.out.println("myThread1");
} finally {
// lock.unlock();
}
}
public void myThread2() {
// 这个方法不会被执行,因为 线程t1,未释放锁。
//线程t2会一直等待,直到获取到锁。
lock.lock();
try {
System.out.println("myThread2");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
MyLock myTest = new MyLock();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
myTest.myThread1();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
myTest.myThread2();
}
});
// 程序因为线程t1没有释放锁,所以在执行完10次myMonth1方法后,程序会继续等待,
//不能正常退出。
t1.start();
t2.start();
}
// 线程1结束
// 线程2 被挂起等待。
// 但可以看出,线程1在没有释放锁的情况下,同一个线程可以多次访问。
}
程序修正:
public void myThread2() {
boolean result = false;
try {
result = lock.tryLock(800, TimeUnit.MICROSECONDS);
} catch (Exception e){
}
if (result) {
System.out.println("get the lock");
} else {
System.out.println("can't get the lock");
}
}
// 如果把myMonth2方法改成这样,虽然线程t2还是获取不到锁,
// 但却可以在执行完10次myMonth1方法后,正常退出。
输出信息:
myThread1 * 10 遍
can’t get the lock * 10 遍