1

我正在开发一个演示程序,该程序显示了处理线程的不同方式的影响。我正在使用一个简单的任务,即获取一个 char 数组并构建一个名称,将每个 char 发送到一个 StringBuilder,该 StringBuilder 调用一个名为 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() 语句返回的列表中,但列表为空。

我有一种感觉我错过了一些明显的东西,谁能发现我在这里做错了什么?

4

1 回答 1

1

您在不同的加法器之间共享busyAdding,但锁定并通知每个不同的加法器。

这意味着可能会发生以下情况。假设您创建了三个加法器,“O”、“p”和“s”,第一个可能会进入addLetterSynchWithNotifyAll,经过警卫并将忙碌信号设置为 true。现在,接下来的两个跑步者进入,并被困在后卫身上。当第一个线程存在时,它会通知所有在第一个运行者处等待的线程(目前没有)。您想在对象之间共享信号。

您可以将addLetterSynchWithNotifyAll 设为静态,这意味着同步将在类级别,然后您需要将等待更改为在类上,而不是在实例上。或者您可以创建一个用于同步和等待的通用锁对象。

如果您将方法设为静态,请注意没有其他线程将进入该方法,因为它们现在在同一个对象上同步,并且您无需使用busyAdding进行保护。

于 2012-11-21T22:35:02.600 回答