ReentrantLock可重入锁

Java Lock与synchronized关键字具有相同语义。

ReentrantLock

ReentrantLock类实现了Lock接口,在访问共享资源时提供同步。

顾名思义,ReentrantLock允许线程多次锁定资源

当线程首次进入锁定状态时,hold count设置为1。解锁前线程可再次进入锁定,hold count加1。

每个解锁请求,hold count减少1。

可重入锁提供一个公平参数,该参数遵守锁请求顺序,即,线程解锁资源后,让等待时间最长的线程获取锁。

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.await()作一个等待操作,Condition.signal()唤醒一个等待。

以上两个都需放在lock.lock()与lock.unlock()间,如同调用wait方法需要一个synchronized同步块。

await()与wait()都能自我唤醒,前提是,设置了等待参数[await(time, unit)]。