簡體   English   中英

使用 Java Monitor 而不是 Semaphore

[英]use Java Monitor instead of Semaphore

我有兩個方法, request()release() ,訪問request()每個線程必須控制,直到它調用release() ,這意味着在當前線程還沒有完成之前,沒有其他線程會進入請求方法。 我需要同時使用 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

如您所見,這是正確的,因為每個線程僅在另一個線程退出時才進入。 使用監視器有問題,代碼如下:

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();
}

這是發生了什么:

  • P1 請求,在執行 request() 時鎖定。 一旦您的同步部分結束(即函數結束),您就釋放鎖。

  • P1 延遲()

  • 當 P1 延遲時,另一個進程可以執行 request() 方法。

  • 現在,在 P1 的請求代碼執行所需的幾毫秒內,很可能在此期間的任何事情都會命中 P1 擁有的鎖,並且正在等待。

    • 這似乎是 P4 進入下一個的原因(P2 和 P3 正在等待通知,因為他們在那個時間段內鎖定了)。

總之,您的 delay() 必須在代碼的同步部分內,或者您需要在更高級別使用監視器(或僅使用可重入鎖/信號量)

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

synchronized(lock) {...}塊在進入塊時獲取與鎖關聯的互斥lock ,並在離開塊時釋放互斥鎖。

您不能使用synchronized來完全按照您的要求執行操作,因為無法將獲取鎖與釋放鎖分開。

您可以在代碼中將同步移動到更高級別(即始終執行此操作):

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

但是,如果很難將同步保留在原處,那么您將不得不繼續使用信號量(這不是一個壞主意),或者使用類似的東西。


您可以使用java.util.concurrent.locks.ReentrantLock而不是使用Semaphore 對於您的問題,這是一個稍微輕量級的解決方案,但可能還不夠“輕”以產生影響。

你為什么要把信號量換成別的東西?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM