多线程:了解信号量行为

时间:2014-04-03 10:19:05

标签: java multithreading locking deadlock semaphore

我正在努力熟悉信号量的概念。出于这个原因,我编写了一个名为MySemaphore的简单信号量类和一个使用所述信号量进行互斥的测试类MutexThread。请在本文的底部找到我的课程代码。

在我的MutexThread - 类中,我使用了两个synchronized(mutex)块,以确保acquire()release()都与我的输出同步执行到{{ 1}}。我得到的结果是预期的:

stdout

然而,有两件事我不明白:

a)如果我在BORN -> Thread-0 born! ACQUIRE -> Thread-0 is entering critical section! BORN -> Thread-1 born! BORN -> Thread-2 born! BORN -> Thread-3 born! BORN -> Thread-4 born! BORN -> Thread-5 born! BORN -> Thread-6 born! BORN -> Thread-7 born! BORN -> Thread-8 born! BORN -> Thread-9 born! RELEASE -> Thread-0 is leaving critical section! INFO -> Thread-0 holdsLock: false ACQUIRE -> Thread-1 is entering critical section! RELEASE -> Thread-1 is leaving critical section! INFO -> Thread-1 holdsLock: false ACQUIRE -> Thread-2 is entering critical section! RELEASE -> Thread-2 is leaving critical section! INFO -> Thread-2 holdsLock: false [...] - 循环结束时注释掉行System.out.println("INFO -> " + getName() + " holdsLock: " + holdsLock(mutex));,则JVM不再循环通过独立的线程。输出现在看起来像这样:

while

这是为什么?为什么只有BORN -> Thread-0 born! ACQUIRE -> Thread-0 is entering critical section! BORN -> Thread-1 born! BORN -> Thread-2 born! [...] BORN -> Thread-9 born! RELEASE -> Thread-0 is leaving critical section! ACQUIRE -> Thread-0 is entering critical section! RELEASE -> Thread-0 is leaving critical section! ACQUIRE -> Thread-0 is entering critical section! [...] 锁定和解锁Thread-0 - 对象?当我将MySemaphore设置为任意MAX_PARALLEL_THREADS时也是如此:在这种情况下,JVM将始终循环通过相同的N线程,但创建的其他线程永远不会访问该对象。单行代码(只是将某些信息输出到N)如何产生如此巨大的影响呢?

b)当我使用来自stdout的类Semaphore而不是我自己的java.util.concurrent类的信号量对象时,代码不再有效。它会锁定在两个MySemaphore - 块之间的某个位置。输出如下:

synchronized(mutex)

这是为什么?来自BORN -> Thread-0 born! ACQUIRE -> Thread-0 is entering critical section! BORN -> Thread-1 born! BORN -> Thread-2 born! [...] BORN -> Thread-9 born! [... That's it. Program locks!] 的班级Semaphore与我自己的班级有何不同?这怎么会导致锁定?

我的信号量类代码:

java.util.concurrent

我的测试类代码:

public class MySemaphore
{
    private int value;

    public MySemaphore(int value)
    {
        this.value = value;
    }

    public synchronized void acquire()
    {
        while(value <= 0)
        {
            try
            {
                wait();
            }
            catch (InterruptedException ex) {}
        }

        value--;
    }

    public synchronized void release()
    {
        value++;
        notify();
    }
}

2 个答案:

答案 0 :(得分:2)

您在MySemaphore类的内部和外部使用synchronized (mutex)。这总是闻起来很糟糕,当你使用java.util.concurrent.Semaphore而不是MySemaphore时会导致死锁:尝试释放信号量的线程在synchronized (mutex)上被阻塞,而{{1}}由另一个尝试获取的线程拥有信号量。

答案 1 :(得分:1)

语句System.out.println(...)是一个IO操作。 IO操作基本上非常慢,通常还包括阻止当前线程,直到IO操作完成。

如果线程因IO操作而被阻塞,则其他等待线程有机会再次获得处理器时间并运行。这正是您在代码中执行IO操作时看到的行为。

从代码中删除IO操作时,当前线程直接继续acquire互斥锁。只有一个非常小的时间窗口,这个线程可能会被系统阻塞,因此另一个线程会运行。但是......这个时间窗口,因此存在这个小机会。

此外,您只通知release方法中的一个主题。如果您通知所有等待的线程,机会会更好,因此系统有更多选择。这样做:

public synchronized void release() {
    value++;
    notifyAll();
}

立即运行您的程序一段时间。我确实在运行的线程中得到了一些更改。

相关问题