欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JUC - Lock接口

程序员文章站 2022-05-05 16:34:20
...

Lock接口详解:

JUC - Lock接口

  1. 什么是Lock :
    JUC - Lock接口

  2. 为什么要使用Lock:(不是已经有了synchronized了?)

    • Java中的锁有两种,synchronized与Lock。因为使用synchronized并不需要显示地加锁与解锁,所以往往称synchronized为隐式锁,而使用Lock时则相反,所以一般称Lock为显示锁 也就是需要手动的进行加锁和 释放锁
    • synchronized修饰方法或语句块,所有锁的获取和释放都必须出现在一个块结构中。当需要灵活地获取或释放锁时,synchronized显然是不符合要求的。Lock接口的实现允许锁在不同的范围内获取和释放,并支持以任何顺序获取和释放多个锁
    • Lock 实现提供了使用 synchronized 方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁尝试 (tryLock())、一个获取可中断锁的尝试 (lockInterruptibly()) 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit))。
    • Lock 类还可以提供与隐式监视器锁完全不同的行为和语义,如保证排序、非重入用法或死锁检测。如果某个实现提供了这样特殊的语义,则该实现必须对这些语义加以记录
    • 总结起来就是一句话 Lock实现比synchronized更灵活 能够以更加面向对象的方式实现锁
  3. Lock锁的使用:

    • 多线程经典问题就是买票问题:以往都是使用synchronized进行实现的 :(30张票 3个线程售票 防止超卖 使用synchronized 比较的简单)

      class Ticket {
          private int number = 30;
          public synchronized void sale() {
              synchronized (this) {
              	 if (number > 0) {
                  	System.out.println(Thread.currentThread().getName()
                          + "\t 卖出" + number-- + "号票\t还剩" + number);
              	}
              }    
          }
      }
      
      
      public static void main(String[] args) throws Exception {
              Ticket ticket = new Ticket();
              new Thread(()->{for (int i = 1; i <= 40; i++) ticket.saleBySync(); },"AA").start();
              new Thread(()->{for (int i = 1; i <= 40; i++) ticket.saleBySync(); },"BB").start();
              new Thread(()->{for (int i = 1; i <= 40; i++) ticket.saleBySync(); },"CC").start();
          }
      
    • 另一种形式Lock(需要手动的进行加锁 和 锁的释放)

          private Lock lock = new ReentrantLock();
          public void saleByLock() {
              lock.lock();
              try {
                  if (number > 0) {
                      System.out.println(Thread.currentThread().getName()
                              + "\t 卖出" + number-- + "号票\t还剩" + number
                      );
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  lock.unlock();
              }
          }
      
  4. ReentrantLock详解:
    JUC - Lock接口

    • ReentrantLock是一个可重入的互斥锁。顾名思义,“互斥锁”表示在某一时间点只能被同一线程所拥有。“可重入”表示锁可被某一线程多次获取 当锁没有被某一线程占有时,调用lock()方法的线程将成功获取锁。可以使用isHeldByCurrentThread()getHoldCount()方法来判断当前线程是否拥有该锁。

    • ReentrantLock既可以是公平锁又可以是非公平锁。当此类的构造方法ReentrantLock(boolean fair) 接收true作为参数时,ReentrantLock就是公平锁,线程依次排队获取公平锁,即锁将被等待最长时间的线程占有。与默认情况(使用非公平锁)相比,使用公平锁的程序在多线程环境下效率比较低。而且公平锁不能保证线程调度的公平性,tryLock方法可在锁未被其他线程占用的情况下获得该锁
      JUC - Lock接口

    • Lock使用:

      class X {
         private final ReentrantLock lock = new ReentrantLock();
         // ...
       
         public void m() {
           lock.lock();  // block until condition holds
           try {
             // ... method body
           } finally {
             lock.unlock()
           }
         }
       }
      

5.总结

  • 与synchronized 相比ReentrantLock的使用更灵活。Lock接口的实现允许锁在不同的范围内获取和释放,并支持以任何顺序获取和释放多个锁。
  • ReentrantLock具有与使用 synchronized 相同的一些基本行为和语义,但功能更强大。包括提供了一个非块结构的获取锁尝试 (tryLock())、一个获取可中断锁的尝试 (lockInterruptibly()) 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit))。
  • ReentrantLock具有synchronized所没有的许多特性,比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者轮询锁。
  • ReentrantLock可伸缩性强,应当在高度争用的情况下使用它