我在圣诞老人的工作室中遇到了一个问题,在送礼之前必须要做很多事情。其中一个要求就是11个精灵成功制作了100件礼物。我的主要课程如下:
public class Main {
public volatile int toyList = 100;
public int getToyList() {
return toyList;
}
public void setToyList(int toyList) {
this.toyList = toyList;
}
public static void main(String[] args){
Main main = new Main();
...
Elf elfOne = new Elf("Jim", main);
Elf elfTwo = new Elf("John", main);
Elf elfThree = new Elf("Eamonn", main);
Elf elfFour = new Elf("Eoin", main);
Elf elfFive = new Elf("Ronan", main);
Elf elfSix = new Elf("Seamus", main);
Elf elfSeven = new Elf("Rebecca", main);
Elf elfEight = new Elf("Orla", main);
Elf elfNine = new Elf("Tina", main);
Elf elfTen = new Elf("Filly", main);
Elf elfEleven = new Elf("Jess", main);
...
elfOne.start();
elfTwo.start();
elfThree.start();
elfFour.start();
elfFive.start();
elfSix.start();
elfSeven.start();
elfEight.start();
elfNine.start();
elfTen.start();
elfEleven.start();
}
我的Elf课程看起来像这样:
public class Elf extends Thread{
Semaphore semaphore = new Semaphore(11);
Random rng = new Random();
String name;
Main main;
public Elf(String name, Main main){
this.name = name;
this.main = main;
}
public void run(){
while(true){
try {
semaphore.acquire();
makeToy();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
private void makeToy() throws InterruptedException{
if (main.getToyList() != 0){
if(rng.nextInt(99) <= 4){ //5% chance to fail
System.out.println(name + " failed in making a toy!");
}else{
main.setToyList(main.getToyList() - 1);
System.out.println(name + " created a toy! Toys remaining: " + main.getToyList());
}
} else {
wakeSanta();
}
}
private void wakeSanta() throws InterruptedException {
}
}
我得到的样本输出是:
...
Eamonn created a toy! Toys remaining: 6
Jim created a toy! Toys remaining: 7
Eoin created a toy! Toys remaining: 8
Jess created a toy! Toys remaining: 9
Filly created a toy! Toys remaining: 0
我想得到的输出是它随机的Elf从100到0顺序倒计时。这是一个简单的事情,我的信号量获取是在错误的地方还是我完全误解了信号量?
答案 0 :(得分:2)
是的,您的信号量未正确使用。信号量实例可以授予许多权限。
acquire()
权限 - 阻止,直到权限可用,然后减少可用权限的数量。 release()
权限 - 增加可用权限的数量。 在您的代码中,每个线程都有自己的信号量,并具有11
权限。首先,作为一种同步手段,信号量通常只有在多个线程共享时才有意义。其次,如果你想让资源(在你的情况下toyList
)完全排他性,那么信号量应该只提供一个权限。
这样想:为了制造玩具,精灵需要使用机器。现在你的场景要求只有一个精灵可以同时制作一个玩具,这相当于只有一个玩具制造机器的车间。所以 - 在代码中 - 你需要创建一台机器的等价物:一个Semaphore
- 只有一个权限的实例。好的,我们将Semaphore
放在Main
- 类:
public class Main {
Semaphore semaphore = new Semaphore(1);
// the rest of Main is kept the same
}
现在精灵需要访问玩具制造机器,因此我们使用semaphore.acquire()
代替main.semaphore.aquire()
。但是如果机器在调用run()
时被占用并且从未被释放,那么第一个开始工作的精灵将永远不会让任何其他精灵拥有该机器。因此圣诞老人对如何使用机器制定了一些规则:
因此run()
和makeToy()
的代码更改如下:
public void run(){
while(true){ // sidenote: I would rearrange the nesting: Wrap the while-loop in the try-catch
try {
makeToy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void makeToy() throws InterruptedException{
main.semaphore.acquire();
if (main.getToyList() != 0){
if(rng.nextInt(99) <= 4){ //5% chance to fail
System.out.println(name + " failed in making a toy!");
}else{
main.setToyList(main.getToyList() - 1);
System.out.println(name + " created a toy! Toys remaining: " + main.getToyList());
}
} else {
wakeSanta();
}
main.semaphore.release();
}
如果您想知道何时使用具有多个权限的信号量,请举例说明:
假设您拥有经常阅读且偶尔会被写入的资源。因为读取不会改变值(即,改变状态),所以让多个线程同时读取该值是安全的。假设我们有n
个线程和一个具有n
权限的信号量。一个线程现在必须获得一个读取权限 - 这意味着所有线程可以同时读取,但它必须获得允许写入的所有n
权限 - 这意味着当一个线程正在写入时,没有其他线程可以访问资源(既不读也不写)。
就我个人而言,我认为获得并发布一个信号量只有synchronized
块的单一权限。但是,请注意用户 Ralf 在注释中指出的重要区别:任何线程都可以释放信号量,无论哪个线程实际获得了权限。信号量更具表现力 - 缺点是更容易使用它们。
答案 1 :(得分:1)
从概念上讲,信号量维护一组许可证。信号量只保留可用数量的计数并相应地起作用。
调用acquire()将阻塞,直到许可可用(大于零)然后接受(倒数一)。每个版本()都会添加许可证。
你可以认为二年级学生是一个控制人们在门上访问的守卫(需要控制线程访问的逻辑)。他有一些钥匙(许可证)进入这扇门()。
当一个人(线程)需要进入这扇门时,他需要一把钥匙(acquire()
),或者他等到他拿到钥匙(线程挡)。一个人离开门后,他应该给予密钥(release()
)以便其他人可以获得。
因此,在现有代码中,每个线程都有11个密钥,这是没有意义的,因为现在每个人都有一个拥有11个密钥的独立守卫。
如果您需要同时只控制一个线程makeToy()
。您应该只使用一个许可证使用信号量。
Semaphore semaphore = new Semaphore(1);
这应该是线程共享的公共信号量,所以你可能不应该在线程中实例化信号量。
你可以试试这个:
Main main = new Main();
Semaphore semaphore = new Semaphore(1,true);//Only allow one thread to enter,so totally one permit is avaiable, and fairness =true
Elf elfOne = new Elf("Jim", main,semaphore);//pass the semaphore to all the threads
Elf elfTwo = new Elf("John", main,semaphore);
Elf elfThree = new Elf("Eamonn", main,semaphore);
Elf elfFour = new Elf("Eoin", main,semaphore);
.....
public class Elf extends Thread{
Semaphore semaphore;
Random rng = new Random();
String name;
Main main;
public Elf(String name, Main main,Semaphore semaphore){// need semaphore in constructors
this.name = name;
this.main = main;
this.semaphore=semaphore;
}
以下是更改代码的输出:
...
Filly created a toy! Toys remaining: 79
Eamonn created a toy! Toys remaining: 78
Eoin created a toy! Toys remaining: 77
Jess created a toy! Toys remaining: 76
Ronan created a toy! Toys remaining: 75
Jim failed in making a toy!
John created a toy! Toys remaining: 74
Seamus created a toy! Toys remaining: 73
Rebecca created a toy! Toys remaining: 72
...