1. 程式人生 > >測試併發應用 (一)監控Lock介面

測試併發應用 (一)監控Lock介面

宣告:本文是《 Java 7 Concurrency Cookbook 》的第八章, 作者: Javier Fernández González 譯者:鄭玉婷  

校對:方騰飛

監控Lock介面

Lock 介面是Java 併發 API提供的最基本的機制來同步程式碼塊。它允許定義臨界區。臨界區是程式碼塊可以共享資源,但是不能被多個執行緒同時執行。此機制是通過Lock 介面和 ReentrantLock 類實現的。

在這個指南,你將學習從Lock物件可以獲取的資訊和如何獲取這些資訊。

準備

指南中的例子是使用Eclipse IDE 來實現的。如果你使用Eclipse 或者其他的IDE,例如NetBeans, 開啟並建立一個新的java專案。

怎麼做呢…

按照這些步驟來實現下面的例子:

package tool;

import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//1.   建立一個類,名為 MyLock ,擴充套件 ReentrantLock 類。
public class MyLock extends ReentrantLock {

	// 2. 實現 getOwnerName() 方法。此方法使用Lock類的保護方法 getOwner(), 返回控制鎖的執行緒(如果存在)的名字。
	public String getOwnerName() {
		if (this.getOwner() == null) {
			return "None";
		}
		return this.getOwner().getName();
	}

	// 3. 實現 getThreads() 方法。此方法使用Lock類的保護方法 getQueuedThreads(),返回在鎖裡的執行緒的 queued
	// list。
	public Collection<Thread> getThreads() {
		return this.getQueuedThreads();
	}

	// 4. 建立一個類,名為 Task,實現 Runnable 介面.
	public class Task implements Runnable {

		// 5. 宣告一個私有 Lock 屬性,名為 lock。
		private Lock lock;

		// 6. 實現類的建構函式,初始化它的屬性值。
		public Task(Lock lock) {
			this.lock = lock;
		}

		// 7. 實現 run() 方法。建立迭代5次的for迴圈。
		@Override
		public void run() {
			for (int i = 0; i < 5; i++) {

				// 8. 使用lock()方法獲取鎖,並列印一條資訊。
				lock.lock();
				System.out.printf("%s: Get the Lock.\n", Thread.currentThread()
						.getName());

				// 9. 讓執行緒休眠 500 毫秒。使用 unlock() 釋放鎖並列印一條資訊。
				try {
					TimeUnit.MILLISECONDS.sleep(500);
					System.out.printf("%s: Free the Lock.\n", Thread
							.currentThread().getName());
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					lock.unlock();
				}
			}
		}
	}

	// 10. 建立例子的主類通過建立一個類,名為 Main 並新增 main()方法。
	public static void main(String[] args) throws Exception {

		// 11. 建立 MyLock 物件,名為 lock。
		MyLock lock = new MyLock();

		// 12. 建立有5個Thread物件的 array。
		Thread threads[] = new Thread[5];

		// 13. 建立並開始5個執行緒來執行5個Task物件。
		for (int i = 0; i < 5; i++) {
			Task task = lock.new Task(lock);
			threads[i] = new Thread(task);
			threads[i].start();
		}

		// 14. 建立迭代15次的for迴圈。
		for (int i = 0; i < 15; i++) {

			// 15. 把鎖的擁有者的名字寫入操控臺。
			System.out.printf("Main: Logging the Lock\n");
			System.out.printf("************************\n");
			System.out.printf("Lock: Owner : %s\n", lock.getOwnerName());

			// 16. 顯示鎖queued的執行緒的號碼和名字。
			System.out.printf("Lock: Queued Threads: %s\n",
					lock.hasQueuedThreads()); // 譯者注:加上 System
			if (lock.hasQueuedThreads()) {
				System.out.printf("Lock: Queue Length: %d\n",
						lock.getQueueLength());
				System.out.printf("Lock: Queued Threads: ");
				Collection<Thread> lockedThreads = lock.getThreads();
				for (Thread lockedThread : lockedThreads) {
					System.out.printf("%s ", lockedThread.getName());
				}
				System.out.printf("\n");
			}

			// 17. 顯示關於Lock物件的公平性和狀態的資訊。
			System.out.printf("Lock: Fairness: %s\n", lock.isFair());
			System.out.printf("Lock: Locked: %s\n", lock.isLocked());
			System.out.printf("************************\n");

			// 18. 讓執行緒休眠1秒,併合上類的迴圈。
			TimeUnit.SECONDS.sleep(1);
		}
	}
}

它是如何工作的…

在這個指南里,你實現的MyLock類擴充套件了ReentrantLock類來返回資訊,除此之外獲得不到這些資訊 ,因為ReentrantLock 類裡的資料都是保護型別的。 通過MyLock類實現的方法:

  • getOwnerName():只有唯一一個執行緒可以執行被Lock物件保護的臨界區。鎖儲存了正在執行臨界區的執行緒。此執行緒會被ReentrantLock類的保護方法 getOwner()返回。 此方法使用 getOwner() 方法來返回執行緒的名字。
  • getThreads():當執行緒正在執行臨界區時,其他執行緒嘗試進入臨界區就會被放到休眠狀態一直到他們可以繼續執行為止。ReentrantLock類保護方法getQueuedThreads() 返回 正在等待執行臨界區的執行緒list。此方法返回 getQueuedThreads() 方法返回的結果。

我們還使用了 ReentrantLock 類裡實現的其他方法:

  • hasQueuedThreads():此方法返回 Boolean 值表明是否有執行緒在等待獲取此鎖
  • getQueueLength(): 此方法返回等待獲取此鎖的執行緒數量
  • isLocked(): 此方法返回 Boolean 值表明此鎖是否為某個執行緒所擁有
  • isFair(): 此方法返回 Boolean 值表明鎖的 fair 模式是否被啟用

更多…

ReentrantLock 類還有其他方法也是用來獲取Lock物件的資訊的:

  • getHoldCount(): 返回當前執行緒獲取鎖的次數
  • isHeldByCurrentThread(): 返回 Boolean 值,表明鎖是否為當前執行緒所擁有

參見