Java多线程:Lock显示锁详解

培训教学 潘老师 6个月前 (11-10) 136 ℃ (0) 扫码查看

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.panziye.com/teach/10948.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】