java并发之Lock接口的深入讲解

 更新时间:2021年08月04日 10:36:07   作者:毅航同学  
从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问.那就是Lock,这篇文章主要给大家介绍了关于java并发之Lock接口的相关资料,需要的朋友可以参考下

Juc中各种各样锁信息

在java的juc包为我们提供了各种各样的锁信息。如果细心来看其大部分都会实现一个名为LOCK的接口信息本文皆在帮你回顾Lock信息;

通过本文你将了解到如下内容:

1.Lock和synchronized的对比

2.Lock中常见API的总结

synchronized面临缺点

锁的出现主要是为了保证在并发访问共享资源时不出现错。 在java中如果不考虑性能损耗问题,那么对共享的资源信息加上synchrionzed关键字基本就可以解决大多数并发带来的问题,但是也随之带来灵活性和效率上的问题:

 效率方面:

1.     此种情况下锁的释放情况较少,很容易到导致一直独占资源而导致性能的下降。

2.     当我们试图获取锁时不能直接指定具体条件

3.    不能中断正在试图获得锁的线程

灵活性:

 1.  当获得锁资源后,无法得知是否获得锁信息

 2.  仅当程序异常或顺利执行完时才会释放锁信息,缺乏主动释放锁的时机。

不适用的场景

场景1 :

当我们使用synchronized时,假如某线程获取到锁之后由于要等待IO或者其他原因进入阻塞状态,同时未释放锁信息,那么此时其他线程就只能一直等待。所以此时就需要synchronized有一种机制:避免等待的线程一直无期限地等待下去。
场景2 :

在读文件信息形式,不同线程的写操作是相互冲突的。但是读操作并不会导致冲突。如果我们不加考虑的为资源信息加上synchronized关键字,那么当多线程同时操作时,只有一个线程可以获取到资源,其他未获得锁信息的线程只能进入等待状态,从而导致读写效率不高。

Lock接口

Lock接口是对关键字synchronized的补充和扩展,它允许我们可以在线程安全的情况下更加灵活的操作共享资源信息。

常见用法:

Lock最佳实践:

1.lock(),unlock()

一般来说,使用Lock必须在try…catch…块中进行,并且将释放锁的操作放在finally块中进行。这是因为lock并不会像synchronized那样在异常时释放锁,所以必须保证有手动释放的过程,这样才能保证其它线程有获取锁的机会。

//  加锁
lock.lock();
try{
    //处理任务
}catch(Exception ex){
​
}finally{
    //释放锁  (锁的释放一般放入到finally块中进行,这样保证了总会对锁信息进行释放)
    lock.unlock();   
}

2. tryLock() & tryLock(long time, TimeUnit unit)

tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true;如果获取失败(即锁已被其他线程获取),则返回false,也就是说,这个方法无论如何都会立即返回即使其无法获取到锁也不会一致等待。

tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false

如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。一般情况下,通过tryLock来获取锁时是这样使用的:

Lock lock = ...;
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){
​
     }finally{
         lock.unlock();   //释放锁
     } 
}else {
    //如果不能获取锁,则直接做其他事情
}

3. lockInterruptibly()  

lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程 正在等待获取锁,则这个线程能够 响应中断,即中断线程的等待状态。

例如,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

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

当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为interrupt()方法只能中断阻塞过程中的线程而不能中断正在运行过程中的线程。而在 synchronized 中,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去,这也就是我们需要手动释放锁的原因。

给出如下的例子来进行验证:创建两个线程来共同争抢lock锁信息

public class LockInterruptibly implements Runnable {
​
    private Lock lock = new ReentrantLock();
public static void main(String[] args) {
    LockInterruptibly lockInterruptibly = new LockInterruptibly();
    Thread thread0 = new Thread(lockInterruptibly);
    Thread thread1 = new Thread(lockInterruptibly);
    thread0.start();
​
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    thread1.start();
    thread1.interrupt();
}
   // 任务执行逻辑
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "尝试获取锁");
        try {
            lock.lockInterruptibly();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到了锁");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "睡眠期间被中断了");
            } finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + "释放了锁");
            }
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "获得锁期间被中断了");
        }
    }
}
​

执行结果:

Thread-0尝试获取锁
Thread-0获取到了锁
Thread-1尝试获取锁
Thread-1获得锁期间被中断了
Thread-0释放了锁

通过结果信息我们可以看出,lockInterruptibly()仅能中断正在等待的线程信息,而无法中断正在运行的线程。

对比 Lock和tryLock的区别

lock和tryLock都可以获取到锁信息,但两者之间还是存在些差异的。 具体如下:

1: lock拿不到锁会一直等待。tryLock是去尝试,拿不到就返回false,拿到返回true。

2: tryLock是可以被打断的,被中断的,lock是不可以。

// 实例代码 
public class LockDemo  implements Runnable{
  static Lock lock1 = new ReentrantLock();
    @Override
    public void run() {
        // 分别演示 lock,trylock区别
        // lock1.lock();
        lock1.tryLock();
        System.out.println("线程 " + Thread.currentThread().getName() + " 获取到锁信息 ");
    }
    
    
    public static void main(String[] args) throws InterruptedException {
        LockDemo r1 = new LockDemo();
        LockDemo r2 = new LockDemo();
        r1.flag = true;
        r2.flag = false;
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
​
        t1.start();
        Thread.sleep(1000);
        // 中断
        t2.start();
        t2.interrupt();
    }
}
​

结果信息:

当执行lock1.lock()时的输出:可以看到lock方法并不能响应中断信息,如果不解锁则会一致持有锁信息!

对于tryLock而言其可以响应中断

总结

本篇对Lock接口中常用到的Api进行了分析和总结,同时分析了Lock接口和synchronized关键之间的关系,希望对你能有所启发.

到此这篇关于java并发之Lock接口的文章就介绍到这了,更多相关java并发Lock接口内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Netty分布式ByteBuf使用的底层实现方式源码解析

    Netty分布式ByteBuf使用的底层实现方式源码解析

    这篇文章主要为大家介绍了Netty分布式ByteBuf使用底层实现方式源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • java字符串转数字及各种数字转字符串的3种方法

    java字符串转数字及各种数字转字符串的3种方法

    这篇文章主要介绍了java字符串转数字及各种数字转字符串的3种方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • Java 线程池框架

    Java 线程池框架

    本文主要介绍了Java 线程池框架的相关知识。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • 如何使用Spring Cloud Feign日志查看请求响应

    如何使用Spring Cloud Feign日志查看请求响应

    这篇文章主要介绍了如何使用Spring Cloud Feign日志查看请求响应,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • System.getProperty(“line.separator“)含义及意义详解

    System.getProperty(“line.separator“)含义及意义详解

    这篇文章主要介绍了System.getProperty(“line.separator“)含义,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • IDEA中try catch抛异常快捷键分享

    IDEA中try catch抛异常快捷键分享

    在编写Java代码时,使用IDEA的快捷键CTRL+ALT+t可以快速生成try..catch语句块,有效提高编码效率,首先选择需要处理的代码片段,然后按下快捷键,选择try/catch选项,即可自动包围选中代码,这一快捷操作简化了异常处理步骤,减少了手动编写的时间,是编程中的实用技巧
    2024-10-10
  • 妙用Java8中的Function接口消灭if...else

    妙用Java8中的Function接口消灭if...else

    在开发过程中经常会使用if...else...进行判断抛出异常、分支处理等操作。这些if...else...充斥在代码中严重影响了代码代码的美观,本文就妙用Java8中的Function接口消灭if...else,感兴趣的可以了解一下
    2022-01-01
  • spring data JPA 中的多属性排序方式

    spring data JPA 中的多属性排序方式

    这篇文章主要介绍了spring data JPA 中的多属性排序方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • SpringBoot mybatis-plus使用json字段实战指南

    SpringBoot mybatis-plus使用json字段实战指南

    在现代应用开发中经常会使用JSON格式存储和传输数据,为了便捷地处理数据库中的JSON字段,MyBatis-Plus提供了强大的JSON处理器,这篇文章主要给大家介绍了关于SpringBoot mybatis-plus使用json字段的相关资料,需要的朋友可以参考下
    2024-01-01
  • java截取字符串中的指定字符的两种方法(以base64图片为例)

    java截取字符串中的指定字符的两种方法(以base64图片为例)

    本文介绍了使用Java截取字符串中指定字符的方法,通过substring索引和正则实现,文章详细介绍了实现步骤和示例代码,对于想要了解如何使用Java截取字符串指定字符的读者具有一定的参考价值
    2023-08-08

最新评论