Java多线程:Lock显示锁详解

Java lock锁的是什么?

在JDK5中新增了 java.util.concurrent.locks 包下的Lock锁接口,有ReentrantLock实现类等。ReentrantLock锁称为可重入锁。

Lock 实现提供了比 synchronized 关键字更广泛的锁操作,它能以更优雅的方式处理线程同步问题。Lock提供了比synchronized更多的功能。

Lock接口基本的方法

方法名称 描述
void lock() 获得锁。如果锁已经被其他线程获取,则进行等待
boolean tryLock() 只有在调用时才可以获得锁。如果可用,则获取锁定,并立即返回值为true;如果锁不可用,则此方法将立即返回值为false 。
boolean tryLock(long time, TimeUnit unit) 超时获取锁,当前线程在一下三种情况下会返回: 1. 当前线程在超时时间内获得了锁;2.当前线程在超时时间内被中断;3.超时时间结束,返回false.
void lockInterruptibly() 获取锁,如果可用并立即返回。如果锁不可用,那么等待,和 tryLock(long time, TimeUnit unit) 方法不同的是等待时间无限长,但是在等待中可以中断当前线程(响应中断)。
Condition newCondition() 获取等待通知组件,该组件和当前的锁绑定,当前线程只有获得了锁,才能调用该组件的wait()方法,而调用后,当前线程将释放锁。
void unlock() 释放锁。

多线程lock的用法-ReentrantLock的基本使用

我们先来讲下列2种方法实现显示锁的范式书写方法:

  • lock();
  • unlock();
/**
* 显示锁的范式
*/
public class LockDemo {
// 创建
private Lock lock = new ReentrantLock();
private int count = 0;
/**
* 利用显示锁进行count的累加
*/
public void increamentByLock() {
// 显示锁的范式
// 开启锁;
lock.lock();
try {
count++;
} catch (Exception e) {
e.printStackTrace();
} finally {
// 一定要在finally中将锁进行释放,防止出现异常后锁不释放造成的线程阻塞
lock.unlock();
}
System.out.println(count);
}
/**
* 利用synchronized进行count的累加
*/
public synchronized void increamentBySyn() {
count++;
System.out.println(count);
}
}

一般将lock.lock()写在try外面,不建议写try里面,lock.unlock()写在finally中。不管在哪里的代码,只要使用了同一个Lock,都可以进行线程同步。

通过以上我们可以看到:

1、显示锁有其书写范式,一定要遵从该范式进行书写,避免出现异常造成线程阻塞。

2、synchronized内置锁要比显示锁书写要更加简洁。

关于《Lock和Synchronized的具体区别》可直接去查看。

tryLock()

  • tryLock()用来尝试获取锁,如果当前线程没有被其他线程占用,则获取成功,则返回true,否则返回false,代表获取锁失败
  • 相比上面的lock(),他可以返回一个值,让我们知道是否成功获取到锁;进而决定后续程序的行为
  • 它会立刻返回,即便在拿不到锁时,不会一直等待

一般情况下通过tryLock来获取锁时是这样使用的:

Lock lock = ...;
if(lock.tryLock()) {
try{
//处理任务
}catch(Exception ex){

}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}

tryLock(long time,TimeUnit unit)

可以设定超时时间的尝试获取锁,一段时间内等待锁,超时就放弃。

锁的可重入性

锁的可重入性是指,当一个线程获得一个对象锁后,再次请求该对象锁时,可以再次获得该对象的锁。非可重入锁就是最常见的锁,一旦锁被使用,如果没有释放,就不能再使用这个锁了。

如果锁具备可重入性,则称作为可重入锁。像synchronized和 ReentrantLock都是可重入锁,可重入性实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。

举个简单的例子,当一 个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法 method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。

以下代码可以顺利运行下去,代表synchronized里面的锁是具有可重入性的:

public class Test01 {

public synchronized void sm1() {
System.out.println("同步方法1");
sm2();
}

public synchronized void sm2() {
System.out.println("同步方法2");
sm3();
}

public synchronized void sm3() {;
System.out.println("同步方法3");
}

public static void main(String[] args) {
Test01 obj = new Test01();
new Thread(new Runnable() {
@Override
public void run() {
obj.sm1();
}
}).start();
}
}

lockInterruptibly()方法

如果在获取锁的过程中,线程的中断标志位为true,则获取锁失败,且立即报InterruptedException异常,并将中断标志位的值置为false。

对于synchronized内部锁来说,如果一个线程在等待锁,只有两个结果。要么线程获得锁继续执行,要么就保持。

对于Reentrant Lock可重入锁来说,提供了另外一种可能,在等待锁的过程中,程序可以根据需要取消对锁的请求。

由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException

public void method() throws InterruptedException {
lock.lockInterruptibly();
try {
//.....

} finally {
lock.unlock();
}
}

newCondition()方法

synchronized与wait()、notify()这两个方法一起使用可以实现等待/通知模式,Lock锁的newCondition()方法返回Condition对象,也可以实现等待/通知模式

使用notify()通知后,JVM会随机唤醒某个等待的线程,使用Condition类可以进行选择性地通知某和线程。

Condition比较常用的两个方法:await()、singal()。注意,在调用这两个方法前,需要线程持有相关的Lock锁。调用await()后,线程会释放这个锁,调用signal()后,会从当前Condition对象的等待序列中唤醒一个线程。

后面我们会详解该方法,请参考《Java Lock锁Condition接口和newCondition()方法

 总结

以上就是Java多线程:Lock显示锁详解,我们主要讲了Lock接口的使用方式,

原创文章,作者:博主,如若转载,请注明出处:https://www.unmao.com/course/2235.html

下载资源版权归原作者所有,若有侵权,请邮件ziyeblog@126.com及时删除。

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
博主的头像博主管理员
上一篇 2026-07-04 16:42
下一篇 2026-07-04 16:44

相关推荐

发表回复

登录后才能评论
微信
QQ

博主邮箱:ziyeblog@126.com

打赏
展开教程