天天看點

java concurrent之ReentrantLock二者的差別基本用法中斷ReentrantLock公平性

在編碼的過程中,有時候我們不得不借助鎖同步來保證線程安全。synchronized關鍵字在上一篇部落格中已經介紹;自從JDK5開始,添加了另一種鎖機制:ReentrantLock。

二者的差別

1、lock是jdk5之後代碼層面實作的,synchronized是JVM層面實作的。

2、synchronized在出現異常的時候能夠自動釋放鎖,而lock必須在finally塊中unlock()主動釋放鎖,否則會死鎖。

3、在競争不激烈的時候synchronized的性能是比lock好一點的,但是當競争很激烈時synchronized的性能會相對幾十倍的下降,因為lock用了新的鎖機制,新的Lock機制最終歸結到一個原子性操作上。

4、synchronized無法中斷一個正在等候獲得鎖的線程,也無法通過投票得到鎖,如果不想等下去,也就沒法得到鎖;而lock可以。

5、ReentrantLock可以采用FIFO的政策進行競争,更加公平。

基本用法

先寫個簡單的例子看一下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestReentrantLock {
 public static void main(String[] args) {
  final ReentrantLock rLock = new ReentrantLock();
  final Condition condition = rLock.newCondition();
  ExecutorService executorService = Executors.newFixedThreadPool(5);
  Runnable opt = new Runnable() {
   @Override
   public void run() {
    rLock.lock();
    System.out.println(Thread.currentThread().getName()+"--->>lock()");
    try {
     condition.await();
    } catch (InterruptedException e) {
     e.printStackTrace();
    } finally {
     System.out.println(Thread.currentThread().getName()+"--->>unlock()");
     rLock.unlock();
    }
   }
  };
  
  for (int i = 0; i < 4; i++) {
   executorService.submit(opt);
  }
  
  Runnable release = new Runnable() {
   @Override
   public void run() {
    rLock.lock();
    try {
     Thread.sleep(2000);
     System.out.println(Thread.currentThread().getName()+"--->>signalAll()");
     condition.signalAll();
    } catch (InterruptedException e) {
     e.printStackTrace();
    } finally {
     rLock.unlock();
    }
   }
  };
  executorService.submit(release);
  executorService.shutdown();
 }
}
           

運作結果:

pool-1-thread-1--->>lock()

pool-1-thread-2--->>lock()

pool-1-thread-3--->>lock()

pool-1-thread-4--->>lock()

pool-1-thread-5--->>signalAll()

pool-1-thread-1--->>unlock()

pool-1-thread-2--->>unlock()

pool-1-thread-3--->>unlock()

pool-1-thread-4--->>unlock()

上面代碼中有個Condition,它的三個方法await 、 signal 和 signalAll,與基類的wait、notify和notifyAll方法相對應,因為它們不能覆寫Object上的對應方法,是以就起了這三個奇葩的名字。由上面的代碼可以看出ReentrantLock和synchronized用法是基本相同的。

中斷ReentrantLock

執行個體如下:

package co.etc.concurrent;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockSample {
 public static void main(String[] args) {
  testReentrantLock();
 }
 public static void testReentrantLock() {
  final SampleSupportLock support = new SampleSupportLock();
  Thread first = new Thread(new Runnable() {
   public void run() {
    try {
     support.doSomething();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  });
  Thread second = new Thread(new Runnable() {
   public void run() {
    try {
     support.doSomething();
    } catch (InterruptedException e) {
     System.out.println("InterruptedException--->>");
    }
   }
  });
  executeTest(first, second);
 }
 public static void executeTest(Thread a, Thread b) {
  a.start();
  try {
   Thread.sleep(100);
   b.start();
   Thread.sleep(1000);
   System.out.println("---->>>interrupt()");
   b.interrupt();
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}
abstract class SampleSupport {
 protected int counter;
 public void startTheCountdown() {
  long currentTime = System.currentTimeMillis();
  for (;;) {
   long diff = System.currentTimeMillis() - currentTime;
   if (diff > 2000) {
    break;
   }
  }
 }
}
class SampleSupportLock extends SampleSupport {
 private final ReentrantLock lock = new ReentrantLock();
 public void doSomething() throws InterruptedException {
  lock.lockInterruptibly();
  System.out.println(Thread.currentThread().getName()
    + "doSomething()--->>");
  startTheCountdown();
  try {
   counter++;
  } finally {
   lock.unlock();
  }
  System.out.println("counter---->>>"+counter);
 }
}
           

運作結果:

Thread-0doSomething()--->>

---->>>interrupt()

InterruptedException--->>

counter---->>>1

運作結果表明第二個線程被中斷了,這是因為我用的是lock.lockInterruptibly();在主線程中我調用了b.interrupt();二synchronized是沒法做到的

公平性

執行個體代碼如下:

import java.util.Collection;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestFairLock {
 private static Lock fairLock = new ReentrantLock2(true);
 private static Lock unfairLock = new ReentrantLock2();
 public static void main(String[] args) {
  TestFairLock testFairLock = new TestFairLock();
  // testFairLock.unfair();
  testFairLock.fair();
 }
 public void fair() {
  System.out.println("fair version");
  for (int i = 0; i < 5; i++) {
   Thread thread = new Thread(new Job(fairLock)) {
    public String toString() {
     return getName();
    }
   };
   thread.setName("" + i);
   thread.start();
  }
  // sleep 5000ms
 }
 public void unfair() {
  System.out.println("unfair version");
  for (int i = 0; i < 5; i++) {
   Thread thread = new Thread(new Job(unfairLock)) {
    public String toString() {
     return getName();
    }
   };
   thread.setName("" + i);
   thread.start();
  }
  // sleep 5000ms
 }
 private static class Job implements Runnable {
  private Lock lock;
  public Job(Lock lock) {
   this.lock = lock;
  }
  @Override
  public void run() {
   for (int i = 0; i < 5; i++) {
    lock.lock();
    try {
     System.out.println("Thread--->>"
       + Thread.currentThread().getName());
    } finally {
     lock.unlock();
    }
   }
  }
 }
 private static class ReentrantLock2 extends ReentrantLock {
  private static final long serialVersionUID = 1773716895097002072L;
  public ReentrantLock2(boolean b) {
   super(b);
  }
  public ReentrantLock2() {
   super();
  }
  public Collection<Thread> getQueuedThreads() {
   return super.getQueuedThreads();
  }
 }
}
           

運作結果

unfair version

Thread--->>0

Thread--->>0

Thread--->>0

Thread--->>0

Thread--->>0

Thread--->>1

Thread--->>1

Thread--->>1

Thread--->>1

Thread--->>1

Thread--->>2

Thread--->>2

Thread--->>2

Thread--->>2

Thread--->>2

Thread--->>3

Thread--->>3

Thread--->>3

Thread--->>3

Thread--->>3

Thread--->>4

Thread--->>4

Thread--->>4

Thread--->>4

Thread--->>4

fair version

Thread--->>0

Thread--->>0

Thread--->>1

Thread--->>3

Thread--->>0

Thread--->>4

Thread--->>2

Thread--->>1

Thread--->>3

Thread--->>0

Thread--->>4

Thread--->>2

Thread--->>1

Thread--->>3

Thread--->>0

Thread--->>4

Thread--->>2

Thread--->>1

Thread--->>3

Thread--->>4

Thread--->>2

Thread--->>1

Thread--->>3

Thread--->>4

Thread--->>2

從運作結果看到用ReentrantLock(boolean fair)建構的鎖,相對ReentrantLock()是更公平的,當fair為true時采用的是FIFO政策,所

以各個線程能夠更平均的配置設定時間。