Java可重入锁ReentrantLock用法

Java Lock具有与synchronized关键字相同的基本行为和语义,并对其进行了扩展。

Java中实现线程同步的传统方法是使用synchronized关键字。尽管提供了一定的基本同步,但synchronized关键字的使用却非常严格。

如,一个线程只能获得一次锁定。同步块不提供任何等待队列的机制,且在退出一个线程之后,任何线程都可以获取锁,这可能导致很长一段时间内其他线程的资源匮乏。

ReentrantLock

ReentrantLock类实现了Lock接口,并在访问共享资源时提供对方法的同步。操作共享资源的代码,被一对lock和unlock方法包围,为当前工作线程提供一个锁,并阻塞其他试图锁定共享资源的线程。

顾名思义,ReentrantLock允许线程多次对资源进行锁定当线程首次进入锁定状态时,hold count将设置为1。在解锁之前,线程可以再次进入锁定,且每次hold count都加1。对于每个解锁请求,hold count减少1。

可重入锁还提供了一个公平参数,通过该参数,Lock将遵守锁请求的顺序,即,在线程解锁资源之后,会让等待时间最长的线程获取锁。通过将true传递给锁的构造方法设置公平模式。

public void some_method() 
{ 
    reentrantlock.lock(); 
    try
    { 
      //Do some work 
    } 
    catch(Exception e) 
    { 
      e.printStackTrace(); 
    } 
    finally
    { 
      reentrantlock.unlock(); 
    } 
      
} 


始终在finally块中调用unlock,以确保即使方法出现异常,也可将锁定释放。

ReentrantLock方法

lock():调用lock方法会使hold count加1,且如果共享资源最初是空闲的,则将锁定授予线程。

unlock():调用unlock方法将hold count减1。当该计数达到零时,资源被释放。

tryLock():如果资源没有被其他线程hold,则tryLock将返回true,且hold count将加1。如果资源不是空闲的,则该方法返回false。

tryLock(long timeout, TimeUnit unit):对于该方法,线程等待指定的时间后,放弃获取锁。

lockInterruptible():如果资源空闲时,允许该线程在获取资源时被其他线程中断,则此方法获取锁。这意味着如果当前线程正在等待锁,但其他线程请求该锁,则当前线程将被中断,并立即返回而无需获取锁。

getHoldCount():此方法返回资源上持有的锁数的计数。

isHeldByCurrentThread():如果资源锁由当前线程持有,则此方法返回true。

示例

使用ReentrantLock应遵循的步骤:

1.创建一个ReentrantLock对象。

2.创建一个worker(Runnable Object)执行,并将锁传递给该对象。

3.使用lock()方法获取对共享资源的锁。

4.完成工作后,调用unlock()解除锁定的方法。

import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.locks.ReentrantLock; 
  
class worker implements Runnable 
{ 
 String name; 
 ReentrantLock re; 
 public worker(ReentrantLock rl, String n) 
 { 
  re = rl; 
  name = n; 
 } 
 public void run() 
 { 
  boolean done = false; 
  while (!done) 
  {
   boolean ans = re.tryLock(); 
   if(ans) 
   { 
    try
    { 
     Date d = new Date(); 
     SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); 
     System.out.println("task name - "+ name 
           + " outer lock acquired at "
           + ft.format(d) 
           + " Doing outer work"); 
     Thread.sleep(1500); 
     re.lock(); 
     try
     { 
      d = new Date(); 
      ft = new SimpleDateFormat("hh:mm:ss"); 
      System.out.println("task name - "+ name 
            + " inner lock acquired at "
            + ft.format(d) 
            + " Doing inner work");
      System.out.println("Lock Hold Count - "+ re.getHoldCount());
      Thread.sleep(1500); 
     } 
     catch(InterruptedException e) 
     { 
      e.printStackTrace(); 
     } 
     finally
     { 
      System.out.println("task name - " + name + 
            " releasing inner lock");   
      re.unlock(); 
     } 
     System.out.println("Lock Hold Count - " + re.getHoldCount()); 
     System.out.println("task name - " + name + " work done");  
     done = true; 
    } 
    catch(InterruptedException e) 
    { 
     e.printStackTrace(); 
    } 
    finally
    { 
     System.out.println("task name - " + name + 
           " releasing outer lock");   
     re.unlock(); 
     System.out.println("Lock Hold Count - " + 
            re.getHoldCount());
    } 
   } 
   else
   { 
    System.out.println("task name - " + name + 
           " waiting for lock");
    try
    { 
     Thread.sleep(1000);
    } 
    catch(InterruptedException e) 
    { 
     e.printStackTrace();
    } 
   } 
  } 
 } 
} 
  
public class test 
{ 
 static final int MAX_T = 2; 
 public static void main(String[] args) 
 { 
  ReentrantLock rel = new ReentrantLock(); 
  ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
  Runnable w1 = new worker(rel, "Job1");
  Runnable w2 = new worker(rel, "Job2");
  Runnable w3 = new worker(rel, "Job3");
  Runnable w4 = new worker(rel, "Job4");
  pool.execute(w1);
  pool.execute(w2);
  pool.execute(w3);
  pool.execute(w4);
  pool.shutdown();
 } 
} 

注意事项

在finally块中调用unlock()方法,确保在线程退出之前释放锁定。

构造方法中的公平参数会降低程序的吞吐量。

Condition

Condition是Lock接口其中一个特性,Condition作为两个线程协作的中间人,通过Condition.await()作一个等待操作,通过Condition.signal()唤醒一个等待。他们都必需放在lock.lock()与lock.unlock()之间,就像使用wait方法需要一个synchronized代码块。

await()与wait()的相似之处在于它们都能自我唤醒,前提是,如果设置了等待的时间参数[await(time, unit)]。