繁体   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