首頁  >  文章  >  Java  >  詳解Java多執行緒程式設計中LockSupport類別的執行緒阻塞用法

詳解Java多執行緒程式設計中LockSupport類別的執行緒阻塞用法

高洛峰
高洛峰原創
2017-01-05 15:56:401472瀏覽

LockSupport是用來建立鎖定和其他同步類別的基本執行緒阻塞原語。
LockSupport中的park() 和unpark() 的作用分別是阻塞線程和解除阻塞線程,而且park()和unpark()不會遇到“Thread.suspend 和Thread.resume所可能引發的死鎖”問題。
因為park() 和 unpark()有許可的存在;調用 park() 的線程和另一個試圖將其 unpark() 的線程之間的競爭將保持活性。

基本用法
LockSupport 很類似於二元信號量(只有1個許可證可供使用),如果這個許可還沒有被佔用,當前線程獲取許可並繼續執行;如果許可已經被佔用,當前線程阻塞,等待獲取許可。

public static void main(String[] args)
{
   LockSupport.park();
   System.out.println("block.");
}

   

運行程式碼,可以發現主執行緒一直處於阻塞狀態。因為 許可預設是被佔用的 ,當呼叫park()時取得不到許可,所以進入阻塞狀態。

如下程式碼:先釋放許可,再取得許可,主執行緒能夠正常終止。 LockSupport許可的取得和釋放,一般來說是對應的,如果多次unpark,只有一次park也不會出現什麼問題,結果是許可處於可用狀態。

public static void main(String[] args)
{
   Thread thread = Thread.currentThread();
   LockSupport.unpark(thread);//释放许可
   LockSupport.park();// 获取许可
   System.out.println("b");
}

   

LockSupport是可不重入 的,如果一個執行緒連續2次呼叫 LockSupport .park(),那麼該執行緒一定會一直阻塞下去。

public static void main(String[] args) throws Exception
{
 Thread thread = Thread.currentThread();
  
 LockSupport.unpark(thread);
  
 System.out.println("a");
 LockSupport.park();
 System.out.println("b");
 LockSupport.park();
 System.out.println("c");
}

   

這段程式碼印出a和b,不會印c,因為第二次呼叫park的時候,執行緒無法取得許可出現死鎖。

下面我們來看下LockSupport對應中斷的響應性

public static void t2() throws Exception
{
 Thread t = new Thread(new Runnable()
 {
  private int count = 0;
 
  @Override
  public void run()
  {
   long start = System.currentTimeMillis();
   long end = 0;
 
   while ((end - start) <= 1000)
   {
    count++;
    end = System.currentTimeMillis();
   }
 
   System.out.println("after 1 second.count=" + count);
 
  //等待或许许可
   LockSupport.park();
   System.out.println("thread over." + Thread.currentThread().isInterrupted());
 
  }
 });
 
 t.start();
 
 Thread.sleep(2000);
 
 // 中断线程
 t.interrupt();
 
  
 System.out.println("main over");
}

   

最終執行緒會列印出thread over.true。這說明 執行緒如果因為呼叫park而阻塞的話,能夠回應中斷請求(中斷狀態被設定成true),但是不會拋出InterruptedException 。

LockSupport函數列表

// 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
static Object getBlocker(Thread t)
// 为了线程调度,禁用当前线程,除非许可可用。
static void park()
// 为了线程调度,在许可可用之前禁用当前线程。
static void park(Object blocker)
// 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
static void parkNanos(long nanos)
// 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
static void parkNanos(Object blocker, long nanos)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(long deadline)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(Object blocker, long deadline)
// 如果给定线程的许可尚不可用,则使其可用。
static void unpark(Thread thread)

   


LockSupport示例
對比下面的“示例1”和“示例2”可以更清晰的了解LockSupport的用法。
範例1

public class WaitTest1 {
 
  public static void main(String[] args) {
 
    ThreadA ta = new ThreadA("ta");
 
    synchronized(ta) { // 通过synchronized(ta)获取“对象ta的同步锁”
      try {
        System.out.println(Thread.currentThread().getName()+" start ta");
        ta.start();
 
        System.out.println(Thread.currentThread().getName()+" block");
        // 主线程等待
        ta.wait();
 
        System.out.println(Thread.currentThread().getName()+" continue");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
 
  static class ThreadA extends Thread{
 
    public ThreadA(String name) {
      super(name);
    }
 
    public void run() {
      synchronized (this) { // 通过synchronized(this)获取“当前对象的同步锁”
        System.out.println(Thread.currentThread().getName()+" wakup others");
        notify();  // 唤醒“当前对象上的等待线程”
      }
    }
  }
}

   

範例2

import java.util.concurrent.locks.LockSupport;
 
public class LockSupportTest1 {
 
  private static Thread mainThread;
 
  public static void main(String[] args) {
 
    ThreadA ta = new ThreadA("ta");
    // 获取主线程
    mainThread = Thread.currentThread();
 
    System.out.println(Thread.currentThread().getName()+" start ta");
    ta.start();
 
    System.out.println(Thread.currentThread().getName()+" block");
    // 主线程阻塞
    LockSupport.park(mainThread);
 
    System.out.println(Thread.currentThread().getName()+" continue");
  }
 
  static class ThreadA extends Thread{
 
    public ThreadA(String name) {
      super(name);
    }
 
    public void run() {
      System.out.println(Thread.currentThread().getName()+" wakup others");
      // 唤醒“主线程”
      LockSupport.unpark(mainThread);
    }
  }
}

   

wait讓執行緒阻塞前,必須透過synchronized取得同步鎖。

更多詳解Java多執行緒程式設計中LockSupport類別的執行緒阻塞用法相關文章請關注PHP中文網!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn