Java 信号量是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1221322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does semaphore work?
提问by
Can the semaphore be lower than 0? I mean, say I have a semaphore with N=3 and I call "down" 4 times, then N will remain 0 but one process will be blocked?
信号量可以小于0吗?我的意思是,假设我有一个 N=3 的信号量并且我调用了 4 次“down”,那么 N 将保持为 0 但一个进程将被阻止?
And same the other way, if in the beginning I call up, can N be higher than 3? Because as I see it, if N can be higher than 3 if in the beginning I call up couple of times, then later on I could call down more times than I can, thus putting more processes in the critical section then the semaphore allows me.
同样反过来,如果一开始我打电话,N可以大于3吗?因为在我看来,如果 N 可以大于 3,如果一开始我调用了几次,那么以后我可以调用比我能调用的次数更多,从而在临界区放置更多的进程然后信号量允许我.
If someone would clarify it a bit for me I will much appreciate.
如果有人能为我澄清一下,我将不胜感激。
Greg
格雷格
采纳答案by Tom
Calling down when it's 0 should not work. Calling up when it's 3 does work. (I am thinking of Java).
当它为 0 时调用不应该工作。当它是 3 时调用确实有效。(我在想Java)。
Let me add some more. Many people think of locks like (binary) semaphores (ie - N = 1, so the value of the semaphore is either 0 (held) or 1 (not held)). But this is not quite right. A lock has a notion of "ownership" so it may be "reentrant". That means that a thread that holds a lock, is allowed to call lock() again (effectively moving the count from 0 to -1), because the thread already holds the lock and is allowed to "reenter" it. Locks can also be non reentrant. A lock holder is expected to call unlock() the same number of times as lock().
让我再补充一些。许多人认为锁类似于(二进制)信号量(即 - N = 1,因此信号量的值是 0(持有)或 1(未持有))。但这并不完全正确。锁具有“所有权”的概念,因此它可能是“可重入的”。这意味着持有锁的线程可以再次调用 lock()(有效地将计数从 0 移动到 -1),因为该线程已经持有锁并且可以“重新进入”它。锁也可以是不可重入的。锁持有者应该调用 unlock() 的次数与 lock() 的次数相同。
Semaphores have no notion of ownership, so they cannot be reentrant, although as many permits as are available may be acquired. That means a thread needs to block when it encounters a value of 0, until someone increments the semaphore.
信号量没有所有权的概念,因此它们不能重入,尽管可以获得尽可能多的许可。这意味着线程在遇到值 0 时需要阻塞,直到有人增加信号量。
Also, in what I have seen (which is Java), you can increment the semaphore greater than N, and that also sort of has to do with ownership: a Semaphore has no notion of ownership so anybody can give it more permits. Unlike a thread, where whenever a thread calls unlock() without holding a lock, that is an error. (In java it will throw an exception).
此外,在我所看到的(Java)中,您可以将信号量增加到大于 N,这也与所有权有关:信号量没有所有权的概念,因此任何人都可以给予它更多的许可。与线程不同,当线程在没有持有锁的情况下调用 unlock() 时,这是一个错误。(在java中它会抛出异常)。
Hope this way of thinking about it helps.
希望这种思考方式有所帮助。
回答by Jon Skeet
(Using the terminology from java.util.concurrent.Semaphore given the Java tag. Some of these details are implementation-specific. I suspect your "down" is the Java semaphore's acquire()
method, and your "up" is release()
.)
(使用 java.util.concurrent.Semaphore 中的术语给定 Java 标记。其中一些细节是特定于实现的。我怀疑您的“向下”是 Java 信号量的acquire()
方法,而您的“向上”是release()
。)
Yes, your last call to acquire()
will block until another thread calls release()
or your thread is interrupted.
是的,您最后一次调用acquire()
将阻塞,直到另一个线程调用release()
或您的线程被中断。
Yes, you can call release()
more times, then down more times - at least with java.util.concurrent.Semaphore
.
是的,您可以调用release()
更多次,然后调用更多次 - 至少使用java.util.concurrent.Semaphore
.
Some other implementations of a semaphore may have an idea of a "maximum" number of permits, and a call to release beyond that maximum would fail. The Java Semaphore
class allows a reverse situation, where a semaphore can start off with a negative number of permits, and all acquire()
calls will fail until there have been enough release()
calls. Once the number of permits has become non-negative, it will never become negative again.
信号量的其他一些实现可能有“最大”许可数的概念,并且超过该最大值的释放调用将失败。JavaSemaphore
类允许相反的情况,在这种情况下,信号量可以以负数的许可开始,并且所有acquire()
调用都将失败,直到有足够的release()
调用为止。一旦许可数量变为非负数,它将永远不会再次变为负数。
回答by h0b0
Just see N as the counter that counts your limited resource. Since you can not have a negative number of resources, N remains >= 0. If the number of your available resources changes, the maximum N has to be changed, too. I wouln't consider it good style to increment n without decrementing it first in any other case.
只需将 N 视为计算您有限资源的计数器即可。由于您不能拥有负数的资源,因此 N 保持 >= 0。如果可用资源的数量发生变化,则最大 N 也必须更改。在任何其他情况下,我不会认为增加 n 而不先减少它的好风格。
回答by Makis
Yes, a negative value means you have processes waiting for the semaphore to be released. A positive value means you can call acquire that many times before the semaphore blocks.
是的,负值意味着您有进程等待信号量被释放。正值意味着您可以在信号量块之前多次调用获取。
You could think of the value in this way: a positive number means there are that many resources available. A negative value means there are that many entities needing a resource when all resources are taken at the moment. When you acquire a resource you decrement the value, when you release it you increase the value. If the value is still >= 0 after the decrement you get the resource, otherwise your entity is put into a queue.
您可以这样考虑价值:正数意味着有很多可用资源。负值意味着当所有资源都被占用时,有许多实体需要一个资源。当您获得资源时,您会减少其价值,当您释放它时,您会增加价值。如果在递减后该值仍然 >= 0,您将获得资源,否则您的实体将被放入队列中。
A nice explanation of semaphores in Wikipedia: http://en.wikipedia.org/wiki/Semaphore_(programming)
维基百科中信号量的一个很好的解释:http: //en.wikipedia.org/wiki/Semaphore_(programming)
回答by Princesh
Hi Greg consider following example :
嗨,格雷格考虑以下示例:
public static void main(String [] args) throws InterruptedException {
Semaphore available = new Semaphore(1, true);
available.acquire();
System.out.println("Acquire : " +available.availablePermits());
available.release();
System.out.println("Released : " +available.availablePermits());
available.release();
System.out.println("Released : " +available.availablePermits());
available.release();
System.out.println("Released : " +available.availablePermits());
available.release();
System.out.println("Released : " +available.availablePermits());
available.acquire();
System.out.println("Acquire : " +available.availablePermits());
available.acquire();
System.out.println("Acquire : " +available.availablePermits());
available.acquire();
System.out.println("Acquire : " +available.availablePermits());
available.acquire();
System.out.println("Acquire : " +available.availablePermits());
available.acquire();
System.out.println("Acquire : " +available.availablePermits());
}
If you see the output u will get following :
如果您看到输出,您将获得以下信息:
Acquire : 0
Released : 1
Released : 2
Released : 3
Released : 4
Acquire : 3
Acquire : 2
Acquire : 1
Acquire : 0
And wait is going on.
等待正在进行中。
So basically permit will increase on every release and acquire will decrease it until 0. Once it reached 0 it will wait until release is called on same object :)
所以基本上permit会在每次释放时增加,而acquire会减少它直到0。一旦它达到0,它就会等到释放在同一个对象上被调用:)
回答by user7105566
Using java.util.concurrent.Semaphore
with methods acquire()
and release()
, I think permits will always be >=0
. Let's say you want to synchronize threads so that only 1 thread can be inside for loop. If sem is type of Semaphore
that has initial value 1, this will not work for more than 2 threads.
使用java.util.concurrent.Semaphore
方法acquire()
和release()
,我认为许可将永远是>=0
。假设您要同步线程,以便只有 1 个线程可以在 for 循环内。如果 sem 是Semaphore
具有初始值 1 的类型,则这不适用于 2 个以上的线程。
while(true){
sem.wait(); // wait is acquire
for(int i=0; i<=5; i++){
try {
Thread.sleep(250);
}catch (InterruptedException e) {}
System.out.println("Thread "+ threadname+ " " + i);
}
sem.signal(); // signal is release }
However, you can implement the Semaphore class from java and make your own class that allows this.
但是,您可以从 java 实现 Semaphore 类,并创建自己的类来实现这一点。
package yourpackage;
import java.util.concurrent.Semaphore;
public class SemaphoreLayer {
public Semaphore s=null;
public String name;
private int val;
public SemaphoreLayer(int i){
s=new Semaphore(i); val=i;
}
public void wait(){
try {
val--;
s.acquire();
} catch (InterruptedException e) {
System.out.println("Error signal semaphorelayer");
}}
public void signal(){
if(val<0){val++;}{
s.release();
val++;
}
}
}
Now val can be negative. However, I am not sure that this is completely safe, because if we have signal from one thread and wait from the other and they try val++
and val--
this can be bad. (chances for this are very small but stil they exist, so if you are coding and you have to be 100% no error, I don't recommend using this code )
In conclusion this is why it is better to use concept of monitors in java and key word synchronized.
现在 val 可以为负数。但是,我不确定这是否完全安全,因为如果我们有来自一个线程的信号并从另一个线程等待并且他们尝试val++
,val--
这可能很糟糕。(这种可能性非常小,但仍然存在,所以如果您正在编码并且必须 100% 没有错误,我不建议使用此代码)总而言之,这就是为什么最好使用监视器的概念的原因java和关键字同步。