我正在开发一个演示程序,该程序展示了处理线程的不同方式的影响。我正在使用一个简单的任务,即获取一个 char 数组并构建一个名称,将每个字符发送到 StringBuilder,该 StringBuilder 调用一个名为 NameBuilder 的可运行对象,而 NameBuilder 又使用带有同步方法的 letterAdder 类和一个静态 StringBuilder 变量来保存构建时的名称。此结构大致基于 Bruce Eckel 的“Thinking in Java”中的 WaxoMatic 示例
这是我用于notifyAll()方法的代码:
import java.util.List;
import java.util.concurrent.*;
class LetterAdder{
public static StringBuilder currName = new StringBuilder();
public static boolean busyAdding = false;
public synchronized void addLetterSynchWithNotifyAll(char letter) throws InterruptedException{
while (busyAdding){
System.out.println("Letter " +letter+" has to wait");
wait();
}
busyAdding=true;
System.out.println("About to add " + letter + " - ");
currName.append(letter);
System.out.println("name is now " + currName);
busyAdding=false;
}
public synchronized void doNotifyAll(char letter) throws InterruptedException{
System.out.print(letter + " is notifying all");
notifyAll();
}
}
class NameBuilder implements Runnable{
private LetterAdder adder = new LetterAdder();
private char nextLetter;
public NameBuilder(char nextLetter){
adder = new LetterAdder();
this.nextLetter=nextLetter;
}
public void run(){
try{
adder.addLetterSynchWithNotifyAll(nextLetter);
adder.doNotifyAll(nextLetter);
} catch (InterruptedException e) {
System.out.println("Letter "+nextLetter+" interrupted!");
}
//tie up thread for specified time to ensure other threads go to wait
try{
TimeUnit.SECONDS.sleep(1);
} catch(Exception e){}
}
}
public class NotifyDemo {
public static void main(String[] args) {
char[] chars = {'M', 'a','r','t','i','n'};
ExecutorService pool = Executors.newCachedThreadPool();
for (int i=0; i<chars.length; i++){
NameBuilder builder = new NameBuilder(chars[i]);
pool.execute(builder);
}
System.out.println("\nWill shutdown thread pool in 20 seconds");
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {}
System.out.println("\nAbout to shutdown thread pool ");
List<Runnable> tasks = pool.shutdownNow();
if (!tasks.isEmpty()){
for (Runnable r: tasks){
System.out.println("Uncompleted task: "+r.toString());
}
}
}
}
/** Output
About to add M -
Letter a has to wait
name is now M
Letter r has to wait
M is notifying allLetter t has to wait
About to add i -
Will shutdown thread pool in 20 seconds
Letter n has to wait
name is now Mi
i is notifying all
About to shutdown thread pool
Letter a interrupted!
Letter t interrupted!
Letter n interrupted!
Letter r interrupted!
*/
从输出中的打印语句可以看出,字母在到达 busyAdding 监视器并打印等待消息时会被阻塞,但即使发送了 notifAlly(),它们也永远不会退出等待状态。
此外,我希望中断的线程出现在 shutdownNow() 语句返回的列表中,但该列表为空。
我有一种感觉,我错过了一些明显的东西,有人能发现我在这里做错了什么吗?
最佳答案
您在不同的加法器之间共享 busyAdding,但对每个不同的加法器进行锁定和通知。
这意味着可能会发生以下情况。假设您创建了三个加法器“O”、“p”和“s”,第一个可能会输入 addLetterSynchWithNotifyAll,通过守卫并将忙信号设置为 true。现在,接下来的两个运行者进入,并被困在守卫身上。当第一个线程存在时,它会通知在第一个运行器处等待的所有线程(目前没有)。您想在对象之间共享信号。
您可以将 addLetterSynchWithNotifyAll 设为静态,这意味着同步将在类级别上进行,然后您需要将等待更改为类上的等待,而不是实例上的等待。或者您可以创建一个公共(public)锁对象用于同步和等待。
如果将方法设置为静态,请注意,没有其他线程将进入该方法,因为它们现在在同一对象上同步,并且不需要使用 busyAdding 进行保护。
关于Java 线程不响应notifyAll(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13502654/