2015-10-02 23 views
2

我有兩種方法,request()release(),每次訪問request()直到它調用release()必須採取控制線程,這意味着沒有其他線程將進入請求方法,直到當前線程還沒有結束。 我需要用java監視器和信號量來實現這個行爲。 這是類的線程:採用Java監視器代替信號燈

public class Process implements Runnable 
{ 
private Thread thread; 
private final int ID; 
private Resource g; 
private final int MIN = 1000, MAX = 5000; 

private void delay() 
{ 
    try 
    { 
     Thread.sleep((int) Math.random() * (MAX - MIN) + MIN); 
    } catch(InterruptedException ex){} 
} 

public Process(int ID, Resource g) 
{ 
    this.thread = new Thread(this, "P#" + ID); 
    this.ID = ID; 
    this.g = g; 
} 

public void run() 
{ 
    while(true) 
    { 
     g.request(ID); 
     delay(); 
     g.release(); 
     delay(); 
    } 
}// run 

public void start() 
{ 
    this.thread.start(); 
} 
} 

使用信號燈可以很容易地實現這一行爲,我走在request()方法互斥鎖,並在release()方法釋放:

public class ResourceS implements Resource 
{ 

private Semaphore mutex = new Semaphore(1); 

public void request(int ID) 
{ 
    try 
    { 
     mutex.acquire(); 
    } catch (InterruptedException e){} 
    System.out.println(Thread.currentThread().getName() + " enter"); 
} 

public void release() 
{ 
    System.out.println(Thread.currentThread().getName() + " exit"); 
    mutex.release(); 
} 

} 

在主我推出五線,這裏是輸出:

P#0 enter 
P#0 exit 
P#1 enter 
P#1 exit 
P#2 enter 
P#2 exit 
P#3 enter 
P#3 exit 
P#4 enter 
P#4 exit 
P#0 enter 
P#0 exit 
P#1 enter 
P#1 exit 
P#2 enter 
P#2 exit 
P#3 enter 

正如你可以看到它是正確的,因爲每個threa d只有在另一個人退出時才進入。 使用顯示器有一個問題,這裏是代碼:

public class ResourceMonitor implements Resource 
{ 
private Object lock = new Object(); 
private boolean oneInside = false; 

public void request(int ID) 
{ 
    synchronized (lock) 
    { 
     while(oneInside) 
     { 
      try { lock.wait(); } catch(InterruptedException e){} 
     } 
     System.out.println(Thread.currentThread().getName() + " enter"); 
     oneInside = true; 
    } 
} 

public void release() 
{ 
    synchronized(lock) 
    { 
     if (oneInside) 
     { 
      lock.notifyAll(); 
      oneInside = false; 
     } 
     System.out.println(Thread.currentThread().getName() + " exit"); 
    } 
} 
} 

這是使用顯示器的輸出。

P#0 enter 
P#0 exit 
P#4 enter 
P#4 exit 
P#0 enter 
P#0 exit 
P#4 enter 
P#4 exit 
P#0 enter 
P#0 exit 
P#4 enter 
P#4 exit 

只有兩個線程進入和退出,而不是有時的P#4P#1。你知道發生了什麼,以及如何獲得使用信號量的相同輸出?

這裏是主要的,誰想要測試它的代碼:

public static void main(String args[]) 
{ 
    Resource g = new ResourceS(); 

    Process p0 = new Process(0, g); 
    Process p1 = new Process(1, g); 
    Process p2 = new Process(2, g); 
    Process p3 = new Process(3, g); 
    Process p4 = new Process(4, g); 

    p0.start(); 
    p1.start(); 
    p2.start(); 
    p3.start(); 
    p4.start(); 
} 
+0

您可以使用調試器來查看其他線程正在做什麼嗎? (在Eclipse中,我將在調試器中啓動程序,然後等待幾次迭代以確保線程實際上被卡住,然後暫停程序並分別查看每個線程) – immibis

+0

「synchronized」關鍵字未實現監控。可悲的是。 – EJP

+0

@immibis使用調試器,代碼按預期工作,也許代碼正常運行時會出現某種飢餓現象。可能是隊列中的所有線程之間只有兩個線程可以訪問臨界區。 – AR89

回答

0

一個synchronized(lock) {...}部件獲得在進入塊與lock相關聯的互斥,並釋放互斥離開時該塊。

您不能使用​​來完成您所要求的內容,因爲無法分開獲取鎖定和釋放它。

你可以移動同步到一個更高的水平的代碼(即,始終做到這一點):

public void run() { 
    while(true) { 
     synchronized(lock) { 
      g.request(ID); 
      delay(); 
      g.release(); 
     } 
     delay(); 
    } 
} 

但如果它是一個硬性要求離開同步它在哪裏,那麼你要麼繼續使用信號量(這不是一個壞主意),要麼使用類似的東西。


你可以使用java.util.concurrent.locks.ReentrantLock而不是使用Semaphore。這對於你的問題來說是一個輕量級的解決方案,但也許不夠「輕便」來改變你的問題。

爲什麼你想改變信號量的東西?

+0

這是我正在關注的課程的作業。 – AR89

1

這裏發生的事情:

  • P1請求,而執行的請求鎖()。一旦你的同步部分結束(即功能結束),你釋放鎖。

  • P1延遲()

  • 雖然P1的延遲,另一種方法可以執行請求()方法。

  • 現在,在執行P1的請求代碼所需的毫秒內,可能在此期間任何事情都會觸發P1所擁有的鎖定,現在正在等待。

    • 這似乎是P4爲什麼會進入下一步(P2和P3正在等待通知,因爲他們在該時間段內觸發鎖定)。

總之,你的延遲()必須的代碼同步段內或你需要在更高層次上使用監視器(或只是使用重入鎖/信號燈)

額外: 您可能還會發現AtomicBoolean有幫助。

+0

在像Java這樣的語言中沒有像「原子基元」這樣的東西。一些特定於平臺的語言支持這些語言,但AFAIK java不支持。您的鏈接顯示了原子**對象的實現之一** - 每個方法調用都保證是原子或完全線程安全。 – specializt

+0

對,它們是Java中原語的包裝對象。 – ryuu9187

+0

不,他們不是「原始包裝對象」 - 請停止製作,現在變得荒謬可笑。查看「AtomicBoolean」的源代碼並感到驚訝。原子對象類似於本機的處理程序類,所謂的「不安全」對象映射到特定於平臺的某些對象上。沒有什麼被「包裹」。完全一樣。有時你會發現用於表示**「不安全」對象狀態的Java基元,但這是關於它的。 – specializt