我有以下Java代码:
import java.util.concurrent.*;
class Foo{
static Semaphore s = new Semaphore(1);
public void fun(final char c, final int r){
new Thread(new Runnable(){
public void run(){
try{
s.acquire(r);
System.out.println(c+"_"+r);
s.release(r+1);
} catch(Exception e){ e.printStackTrace(); }
}
}).start();
}
}
class ths{
public static void main(String[]args) throws Exception{
Foo f = new Foo();
f.fun('B',2);
f.fun('F',6);
f.fun('A',1);
f.fun('C',3);
f.fun('D',4);
f.fun('E',5);
}
}
理想情况下,这应按顺序打印A_1到F_6并退出,但由于某种原因不会发生。它通常打印A_1和B_2,然后卡住。
我的代码找不到任何明显错误的内容。有什么建议吗?
答案 0 :(得分:8)
基本问题是acquire(int permits)
并不能保证一次获取所有许可证。它可以获得更少的许可,然后在等待其余的时候阻止。
让我们考虑你的代码。例如,当三个许可证可用时,没有什么可以保证它们将被提供给线程C
。实际上,它们可以被赋予线程D
以部分满足其acquire(4)
请求,从而导致死锁。
如果您更改代码,这可以解决我的问题:
public void fun(final char c, final int r){
new Thread(new Runnable(){
public void run(){
try{
while (!s.tryAcquire(r, 1, TimeUnit.MILLISECONDS)) {};
System.out.println(c+"_"+r);
s.release(r+1);
} catch(Exception e){ e.printStackTrace(); }
}
}).start();
}
击> <击> 撞击>
(第二个想法,上面的内容也被打破,因为无法保证正确的线程会获得许可 - 它可以继续尝试并无限期地超时。)
答案 1 :(得分:0)
Semaphore
确实一次获得所有许可,否则它将不是真正的semaphore。但是:Java版本还有一个内部等待队列。并且该队列的行为 NOT 最适合当前的免费资源,但或多或少收集许可,直到可以允许队列中第一个请求为止。但是在线程进入该队列之前,如果可用的许可允许线程完全避免进入队列,则检查完成。
我修改了您的代码以显示该队列行为:
import java.util.concurrent.*;
public class SemaphoreTest{
static Semaphore s = new Semaphore(0);
public void fun(final char c, final int r) throws Exception {
new Thread(new Runnable(){
public void run(){
try{
System.out.println("acquire "+r);
s.acquire(r);
System.out.println(c+"_"+r);
} catch(Exception e){ e.printStackTrace(); }
}
}).start();
Thread.sleep(500);
}
public static void main(String[]args) throws Exception{
SemaphoreTest f = new SemaphoreTest();
f.fun('B',2);
f.fun('F',6);
f.fun('A',1);
f.fun('C',3);
f.fun('D',4);
f.fun('E',5);
while(s.hasQueuedThreads()){
Thread.sleep(1000);
System.out.println("release "+1+", available "+(s.availablePermits()+1));
s.release(1);
}
}
}
基本上已完成以下更改:
Thread.start
之后500毫秒的时间给每个线程“定义”排队顺序。acquire
,但不会调用release
。这将确定性地提供此输出:
acquire 2
acquire 6
acquire 1
acquire 3
acquire 4
acquire 5
release 1, available 1
release 1, available 2
B_2
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
release 1, available 5
release 1, available 6
F_6
release 1, available 1
A_1
release 1, available 1
release 1, available 2
release 1, available 3
C_3
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
D_4
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
release 1, available 5
E_5
release 1, available 1
这意味着:如果
,每个线程都会被唤醒