章
目
录
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接口的使用方式,