我有一个关于管理线程的简单问题。我有3个进程与一个许可共享相同的信号量。在正常情况下,第一个过程采用此许可,并在第二个过程中释放两个许可。第二个过程版本3允许第三个过程。我举了一个例子来说明我的问题。
第一个:
public class Process0 extends Thread{
Semaphore s;
public Process0(Semaphore s){
this.s = s;
}
public void run(){
try {
sleep(20000);
s.acquire();
System.out.println("hello");
} catch (InterruptedException ex) {
Logger.getLogger(Process.class.getName()).log(Level.SEVERE, null, ex);
}
s.release(2);
}
}
第二个过程:
public class Process1 extends Thread{
Semaphore s;
public Process1(Semaphore s){
this.s = s;
}
public void run(){
try {
this.sleep(10000);
s.acquire(2);
System.out.println("Hello 2");
} catch (InterruptedException ex) {
Logger.getLogger(Process1.class.getName()).log(Level.SEVERE, null, ex);
}
s.release(3);
}
}
最后一个:
public class Process2 extends Thread{
Semaphore s;
public Process2(Semaphore s){
this.s = s;
}
public void run(){
try {
System.out.println("Acquire process 3 ");
s.acquire(3);
System.out.println("Hello 3");
} catch (InterruptedException ex) {
Logger.getLogger(Process2.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
问题是。当我运行这三个过程并确保过程3是第一个执行acquire
的过程。我会陷入僵局。进程2从不打印“Hello 3”,进程1从不打印“Hello 2”。为什么?
Semaphore s = new Semaphore(1);
Process0 p = new Process0(s);
Process1 p1 = new Process1(s);
Process2 p2 = new Process2(s);
p.start();
p1.start();
p2.start();
答案 0 :(得分:3)
您的信号量构建为new Semaphore(1)
,只有一个许可证可供获取。由于信号量永远不会有三个许可证,因此呼叫s.acquire(3)
将永远不会返回。 Process
获取单一许可的尝试也会阻止,因为已经订购了收购并且Process2
已到达"首先":
release
方法javadoc声明可以在
其他一些线程调用此信号量的release()方法,然后为当前线程分配许可。
这个最小的单线程示例将向您显示:
Semaphore s = new Semaphore(1);
s.acquire(2);
System.out.println("Didn't deadlock!");
解决方案是使用Semaphore.acquire()
请求一个许可,或Semaphore.acquire(1)
也只请求一个许可。
除非您有非常充分理由滥用Semaphore
,否则您还需要确保获得并发放相同数量的许可。来自Javadoc:
没有要求释放许可证的线程必须通过调用acquire [或线程释放其所有许可证]获得该许可证。通过应用程序中的编程约定来建立信号量的正确使用。
此外,您似乎可能正在使用错误的同步器执行此任务。您可以使用CyclicBarrier
或其他class usable for synchronization。
答案 1 :(得分:0)
我花了一些时间来找出你想要完成的事情。这就是我提出的问题。希望它有所帮助。
问题是你的实现中的线程试图以某种顺序获取锁。所以等待3个许可的线程先等待,然后线程等待2个许可证,显然排队等待他的2个许可证,然后第一个线程只需要1个许可证。有一个许可证,所以它很好。然后它返回2个许可证。不幸的是,接下来是线程等待3个许可证,而不是等待2. Bummer。阻止。那是你观察到的。
如果你让其他线程改变了获取的位置,一切都会好的。来了
s.tryAcquire(int permits)
突然一切正常。
我将根据您的代码制作示例,在忙碌的等待循环中进行1次睡眠以查看正在进行的操作。
import java.util.concurrent.Semaphore;
class Process0 extends Thread {
Semaphore s;
public Process0(Semaphore s){
this.s = s;
}
public void run(){
try {
sleep(20000);
s.acquire();
System.out.println("hello");
} catch (InterruptedException ex) {
System.out.println(Process.class.getName());
}
s.release(2);
System.out.println("released 2");
}
}
class Process1 extends Thread{
Semaphore s;
public Process1(Semaphore s){
this.s = s;
}
public void run(){
try {
this.sleep(10000);
while(!s.tryAcquire(2)) {
System.out.println("Busy waiting for 2 permits");
sleep(1000);
}
System.out.println("Hello 2");
} catch (InterruptedException ex) {
System.out.println(Process.class.getName());
}
s.release(3);
System.out.println("Released 3");
}
}
class Process2 extends Thread{
Semaphore s;
public Process2(Semaphore s){
this.s = s;
}
public void run() {
System.out.println("Acquire process 3 ");
while(!s.tryAcquire(3)) {
System.out.println("Busy waiting for 3 permits");
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Hello 3");
}
}
public class DaemonTest {
public static void main(String[] args) {
Semaphore s = new Semaphore(1);
Process0 p = new Process0(s);
Process1 p1 = new Process1(s);
Process2 p2 = new Process2(s);
p.start();
p1.start();
p2.start();
}
}