[英]Using Semaphores in Java
我感到内地向StackOverflow寻求学校帮助,但是我已经用尽了资源,无法终生解决这一问题。 对于我的课程之一,我需要了解如何在Java中构造和正确使用信号量。 练习之一具有以下代码:
import java.lang.Thread;
import java.util.concurrent.*;
public class ThreadSync
{
private static boolean runFlag = true;
public static void main( String[] args ) {
Runnable[] tasks = new Runnable[37];
Thread[] threads = new Thread[37];
// create 10 digit threads
for (int d=0; d<10; d++) {
tasks[d] = new PrintDigit(d);
threads[d] = new Thread( tasks[d] );
threads[d].start();
}
// create 26 letter threads
for (int d=0; d<26; d++) {
tasks[d+10] = new PrintLetter((char)('A'+d));
threads[d+10] = new Thread( tasks[d+10] );
threads[d+10].start();
}
// create a coordinator thread
tasks[36] = new PrintSlashes();
threads[36] = new Thread( tasks[36] );
threads[36].start();
// Let the threads to run for a period of time
try {
Thread.sleep(50);
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
runFlag = false;
// Interrupt the threads
for (int i=0; i<37; i++) threads[i].interrupt();
}
public static class PrintDigit implements Runnable
{
int digit;
public PrintDigit(int d) { digit=d; }
public void run(){
while (runFlag) {
System.out.printf( "%d\n", digit);
}
}
}
public static class PrintLetter implements Runnable
{
char letter;
public PrintLetter(char c) { letter=c; }
public void run(){
while (runFlag) {
System.out.printf( "%c\n", letter);
}
}
}
public static class PrintSlashes implements Runnable
{
public void run(){
while (runFlag) {
System.out.printf( "%c\n", '/');
System.out.printf( "%c\n", '\\');
}
}
}
}
我只需要添加“与信号量有关的语句”即可修改代码,以便程序反复打印出一个'/',后跟三位数字,然后再打印出一个'\\',再后跟两个字母。
他们给出的示例如下:
/156\BA/376\YZ/654\JK/257\HG/445\DD…
任何帮助将不胜感激。 我通常很擅长自己学习,但是这些线程使我头晕! 谢谢!
我对这名教师的教学方法和编码习惯表示怀疑,但我会回答这个问题。 实际上,我认为问题过于复杂的事实使我更愿意回答这个问题,而不是让您自己解决。
该示例有点违反直觉,因为线程没有用于其正常目的,即允许并发执行,而只是作为理解信号量的练习。 结果,信号量也将不得不以某种非标准的方式使用,作为线程之间的信号,而不是为了正常使用它们来管理资源。 您将了解信号在这种人为情况下的工作原理,但可能最终无法理解信号在正常情况下的用法。 但是,可以通过阅读Semaphore类上的Javadoc来解决此问题,因此请回到讲师的人为情况。
很显然,运行PrintSlashes.run()的线程旨在充当管理器,确定何时运行数字线程以及何时运行字符线程。 它需要告诉数字线程何时可以运行,并且需要告诉字符线程何时可以运行。 另外,它需要知道何时打印了三个数字,还需要知道何时打印了两个字符。 这是需要传输的四段信息,最简单的模型是使用四个信号量对象。
信号量对象应代表以下四件事:
PrintDigit.run()应该在打印每个数字之前从可用的数字信号获得许可。 允许数字可用信号灯将数字打印一次限制为三个。 在打印数字后,该方法应从数字打印的信号量中释放一个许可-注意,而不是可用的数字信号量-表示已经打印了一个数字。 我确定您可以弄清楚PrintLetter.run()应该做什么。 顺便提一句,线程正在获取一个信号量但释放了一个不同的信号量,这是设计此示例的一种方式。 通常,线程释放它们获取的相同信号量。
PrintSlashes.run()应该在打印斜杠后从可用的数字信号灯中释放三个许可,然后在打印反斜杠之前从数字的已打印的信号灯中获取三个许可。 释放可用的三位数允许PrintDigit线程打印三位数,并且等待获取已打印的三位数可确保在继续操作之前先打印三位数。 同样,您应该能够弄清楚在打印反斜杠之后会发生什么。
请注意,数字信号量对象应使用0许可进行初始化,以便数字线程将等待斜杠线程开始。
另外两个警告:
要使代码按示例输出所示工作,您还需要从每个打印的字符串中删除\\n
,否则每个字符将位于不同的行上。 但是,讲师可能希望每个字符都在不同的行上,并给您不好的示例输出。 您必须猜测他真正想要的。
如果要使代码更安全,则可能需要按照以下内容在System.out上进行同步:
但是,您的教练在本练习中可能并不关心该问题。
最后,应进行以下更正以修复代码中的其他不良做法:
不应使用通配符导入,因为您没有从并发包中使用很多类。 我认为,绝对不应使用通配符导入。 通配符的导入会使得很难看清类的来源,从而削弱了代码的可读性,而可读性是代码最重要的一个方面。
有意义的常量,例如此代码中的“ 10”,“ 26”,“ 36”和“ 37”,不应写为文字,而应使用已定义的常量,例如, static final int NUMBER_OF_DIGITS = 10;
。 然后,代码本身可以使用符号NUMBER_OF_DIGITS
,使其更易读且也更易于维护,因为您可以轻松地更改常量的值-例如,如果要将代码转换为八进制,则可以更改为8-而无需担心会错过常量的某些出现。
当有意义的常量具有逻辑关系时,尤其不应将它们写为文字。 在这种情况下,即使static final int NUMBER_OF_CHARACTERS = 36
也不是一个好习惯; 它应该是static final int NUMBER_OF_CHARACTERS = NUMBER_OF_DIGITS + NUMBER_OF_LETTERS;
,使逻辑和数字关系清晰明了。
您是否真的要在上交的作业中进行这些更正,取决于您认为教师对得到良好的更正的反应。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.