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

网友投稿 288 2022-12-22

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

目录Juc中各种各样锁信息synchronized面临缺点Lock接口对比 Lock和tryLock的区别总结

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,也就是说,这个方法无论如何都会http://立即返回即使其无法获取到锁也不会一致等待。

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(TLtrwRWHdJhread.currentThread().getName() + "睡眠期间被中断了");

          } finally {

               lock.unlock();

               SysLtrwRWHdJtem.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关键之间的关系,希望对你能有所启发.

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:springboot自动装配的源码与流程图
下一篇:Spring中单例和多例的深入理解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~