我正在努力熟悉信号量的概念。出于这个原因,我编写了一个名为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();
}
}
答案 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();
}
立即运行您的程序一段时间。我确实在运行的线程中得到了一些更改。