Java中的ReentrantLock

时间:2020-01-09 10:35:10  来源:igfitidea点击:

Java中的ReentrantLock是java.util.concurrent.locks包的一部分,在Java 5中添加了Java并发API,该API还具有其他类,例如ConcurrentHashMap,CountDownLatch。 Java中的ReentrantLock类实现java.util.concurrent.locks.Lock接口并提供许多锁定操作。

Java并发中的ReentrantLock

Java中获取互斥锁的传统方法是使用synced关键字,但是ReentrantLock提供了更大的灵活性。 ReentrantLock允许在不同范围内获取和释放锁,并允许以任意顺序获取和释放多个锁。

通过实现ReentrantLock之类的Lock接口,可以更明确地获取和释放锁。它具有像lock()这样的方法来获取锁,而像unlock()这样的方法来释放锁。

Java中的ReentrantLock提供了许多其他功能,例如公平性策略,中断功能,如果另一个线程未持有该锁则尝试获取锁以及仅在指定时间段内等待锁的线程。

Java中的ReentrantLock通过以下方式使用

public void myMethod(){       
  rentrantlock.lock();
  try {
    ..
    ..
  } catch(Exception exp){
    exp.printStackTrace();
  }finally{
    rentrantlock.unlock();
  }
}

使用ReentrantLock获取锁的常规方法是在try块之前调用lock()方法,然后在try-catch-finally块(或者try-finally块)之后调用它,然后使用finally块来调用unlock()方法。

我们不应在try块中包含对lock()方法的调用,因为在调用lock()方法之前或者获取锁时可能会引发某些异常,这可能导致无法获取锁。但是最终调用了unlock()的块仍将被调用,从而导致IllegalMonitorStateException。

即使在获取锁定后出了点问题,我们也确实需要解锁获取的锁定,这就是为什么对lock()的调用应立即跟随try块以及finally的原因。

为什么称它为ReentrantLock

ReentractLock之所以这样命名,是因为当前持有该锁的线程可以重新输入该锁。有一个与锁相关联的获取计数,每当获取锁时,获取计数就会增加1.
每次调用unlock()方法时,获取计数将减一,并且当计数达到零时,资源将被解锁。

Java ReentrantLock类构造函数

  • ReentrantLock()–创建ReentrantLock的实例。

  • ReentrantLock(boolean fair)–使用给定的公平性策略创建ReentrantLock的实例。

ReentrantLock Java示例

在示例中,执行了四个可运行任务。在方法lockMethod()中再次输入相同的锁。我们可以看到两次调用了unlock()方法来释放锁。
另外,tryLock()方法也用于获取锁,在该示例中,如果线程无法获取锁,它不会等待,如果我们希望添加一个循环,以使该线程继续执行,直到获取一个锁为止。锁。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
  public static void main(String[] args) {
    ReentrantLock rl = new ReentrantLock();
    ExecutorService ex = Executors.newFixedThreadPool(2);
    for(int i = 0; i < 4; i++){
      ex.execute(new RClass("Thread-"+i, rl));
    }
    ex.shutdown();
  }
}

class RClass implements Runnable {
  private String threadName;
  ReentrantLock rl;
  RClass(String threadName, ReentrantLock rl){
    this.threadName = threadName;
    this.rl = rl;
  }
  @Override
  public void run() {
    System.out.println("In run method trying to acquire lock- thread " + threadName);
    //acquiring lock
    boolean flag = rl.tryLock();
    if(flag){
      try {
        System.out.println("Thread " + threadName + " has got lock");
        lockMethod();
      } finally{  	
        rl.unlock();
        System.out.println("Count of locks held by thread " + threadName + 
             " - " + rl.getHoldCount());
      } 
    }else{
       System.out.println("Thread- " + threadName + " not able to acquire lock.");
    }
  }

  public void lockMethod(){
    System.out.println("In lockMethod, thread " + threadName + 
      " is waiting to get lock");            
    rl.lock();
    try {        	
      System.out.println("Count of locks held by thread " + threadName + 
          " - " + rl.getHoldCount());
    } finally{
      rl.unlock();
    }
  }    
}

输出:

In run method trying to acquire lock- thread Thread-0
In run method trying to acquire lock- thread Thread-1
Thread Thread-0 has got lock
Thread- Thread-1 not able to acquire lock.
In lockMethod, thread Thread-0 is waiting to get lock
In run method trying to acquire lock- thread Thread-2
Count of locks held by thread Thread-0 - 2
Thread- Thread-2 not able to acquire lock.
Count of locks held by thread Thread-0 - 0
In run method trying to acquire lock- thread Thread-3
Thread Thread-3 has got lock
In lockMethod, thread Thread-3 is waiting to get lock
Count of locks held by thread Thread-3 - 2
Count of locks held by thread Thread-3 - 0

ReentrantLock类方法

Java中ReentrantLock类的一些重要方法如下:

  • getHoldCount()–查询当前线程对该锁的保持次数。

  • isFair()–如果此锁的公平性设置为true,则返回true。

  • isLocked()–查询此锁是否由任何线程持有。

  • lock()–获取锁。

  • lockInterruptible()–除非当前线程被中断,否则获取锁。

  • tryLock()–仅在调用时没有被另一个线程持有的锁才获取该锁。

  • tryLock(长超时,TimeUnit单位)–如果在给定的等待时间内另一个线程未持有该锁并且当前线程尚未中断,则获取该锁。

  • unlock()–尝试释放此锁。

Java中ReentrantLock的缺点

  • 如果没有以正确的方式使用,而try()方法之前没有立即调用lock()方法,而在finally块中没有解锁,则在抛出任何异常的情况下,锁可能不会被解锁。

  • 必须像在线程上调用lock()方法一样多次显式调用unlock()方法,否则将不会释放锁,从而导致性能问题。

  • 使用许多线程访问的公平锁的程序可能会显示较低的总体吞吐量。