简体   繁体   English

中断线程中的BlockingQueue

[英]Interrupting a BlockingQueue in a Thread

I'm writing a program that has 3 threads. 我正在编写一个具有3个线程的程序。 One reads a text file and inputs the words into a ArraylistBlockingQueue of size 2. The next one takes that list and reverses every other word inside of it. 一个读取一个文本文件,然后将单词输入大小为2的ArraylistBlockingQueue中。下一个获取该列表并反转其中的所有其他单词。 The last thread takes the words and writes them to a new text file. 最后一个线程接收这些单词并将其写入新的文本文件。

I have everything working except I cannot figure out how to interrupt and stop my threads. 除了无法弄清楚如何中断和停止线程之外,我一切正常。 The program writes the text file but never ends. 该程序将写入文本文件,但永远不会结束。

Main method 主要方法

 JFileChooser chooser = new JFileChooser();
    File selectedFile = null;
    File outFile = new File("output.txt");

    int returnValue = chooser.showOpenDialog(null);
    if (returnValue == JFileChooser.APPROVE_OPTION) {
        selectedFile = chooser.getSelectedFile();

    }




    BlockingQueue text = new ArrayBlockingQueue(2);
    BlockingQueue out = new ArrayBlockingQueue(2);

    Thread input = new Thread(new inputClass(selectedFile, text));
    Thread reverse = new Thread(new reverseClass(text, out));
    Thread output = new Thread(new outputClass(out, outFile));


    chooser.setSelectedFile(outFile);
    if (chooser.showSaveDialog(chooser) == JFileChooser.APPROVE_OPTION) {

        input.start();
        reverse.start();
        output.start();


    }

Input 输入项

@Override
public void run() {

    try {
        Scanner s = new Scanner(f);

        while (isRunning) {

            try {
                if(s.hasNext()){
                text.put(s.next());
                } else {
               text.put(END);
            }
            } catch (InterruptedException ex) {

                Logger.getLogger(inputClass.class.getName()).log(Level.SEVERE, null, ex);
            } 

        }

    } catch (FileNotFoundException ex) {
        Logger.getLogger(inputClass.class.getName()).log(Level.SEVERE, null, ex);
    }

Reverse Class @Override public void run() { 反向类@Override public void run(){

    String original;
    String temp = "";
    String character = "";
    int count = 1; // keeps track of whether or not a word should be reversed

    while (isRunning) {

        try {

            original = text.take();
            if(original.equalsIgnoreCase(END)){
                Thread.currentThread().interrupt();
            }
            int length = original.length() - 1;
            //if count is even then the word should be reversed.
            if ((count % 2) == 0) {
                // reverses the original string if a ? or . appears
                if (original.contains("?") || original.contains(".")) {
                    character = original.charAt(length) + "";
                    for (int i = (length - 1); i >= 0; i--) {
                        temp = temp + original.charAt(i);

                    }
                    out.put(temp + character);
                    temp = "";
                    character = "";
                    // reverses the orgininal string if no ? or . appears
                } else {

                    for (int i = length; i >= 0; i--) {
                        temp = temp + original.charAt(i);

                    }
                    out.put(temp);
                    temp = "";
                }
                count++;
            } else {
                out.put(original);
                count++;
            }


        } catch (InterruptedException ex) {

            Logger.getLogger(reverseClass.class.getName()).log(Level.SEVERE, null, ex);

        }
    }
    try {
        out.put(END);
    } catch (InterruptedException ex) {
        Logger.getLogger(reverseClass.class.getName()).log(Level.SEVERE, null, ex);
    }

}

Output Code @Override public void run() { 输出代码@Override public void run(){

    while (isRunning) {

        String s = null;
        try {
            s = out.take();
        } catch (InterruptedException ex) {
            Logger.getLogger(outputClass.class.getName()).log(Level.SEVERE, null, ex);
        }

        try {

            BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));

            try {
                if (s.equalsIgnoreCase(END)) {
                    Thread.currentThread().interrupt();
                }
                writer.write(s + " ");


            }  finally {
                writer.flush();
            }

        } catch (IOException ex) {
            Logger.getLogger(outputClass.class.getName()).log(Level.SEVERE, null, ex);

        }
    }

From the Javadoc: 从Javadoc:

A BlockingQueue does not intrinsically support any kind of "close" or "shutdown" operation to indicate that no more items will be added. BlockingQueue本质上不支持任何类型的“关闭”或“关闭”操作,以指示将不再添加任何项目。 The needs and usage of such features tend to be implementation-dependent. 这些功能的需求和使用往往取决于实现。 For example, a common tactic is for producers to insert special end-of-stream or poison objects, that are interpreted accordingly when taken by consumers. 例如,一种常见的策略是,生产者插入特殊的流尾对象或有毒对象,当消费者采取这种方法时会对其进行相应解释。

I suggest queuing the value null to indicate that the stream is over; 我建议对null进行排队,以指示流已结束。 the reader will need to check whether the value read is null and terminate if it is. 读者将需要检查读取的值是否为null ,如果为null终止。

Since you are reading one word at a time, you can send a flag through the blocking queue. 由于您一次读取一个单词,因此可以通过阻止队列发送一个标志。 Sending the string literal "This queue is terminated" would be sufficient. 发送字符串文字“此队列已终止”就足够了。 Since you are reading only one word at a time, there is no way this string can be tested in your word reverse program. 由于您一次只能读取一个单词,因此无法在单词反向程序中测试此字符串。

Once this is received you can interrupt the word reverser. 收到此消息后,您可以中断单词反向器。 Then send the same literal to the writer. 然后将相同的文字发送给作者。 Once the writer receives the flag you can shut down the writer as well. 编写器收到标志后,您也可以关闭编写器。

AKA "Poison Pill" as stated from Sotirios. 如Sotirios所述,又名“毒丸”。

Example from reverser class: 反向器类的示例:

/**
 * Called when being executed. Reverses a word by taking from intake and
 * places the reversed word into store
 */
@Override
public void run() {
    boolean isInterrupted = false;
    while (!isInterrupted) {
        try {
            StringBuilder str = new StringBuilder(intake.take());

            //Exit condition
            if (str.toString().equalsIgnoreCase(END_FLAG)) {
                Thread.currentThread().interrupt();
            }

            //If it is a word to be reversed, then reverse it
            if (oddWord % 2 == 1) {
                str = reverseWord(str);
            }

            //Put word in queue and increment counter
            store.put(str);
            ++oddWord;
        } catch (InterruptedException ex) {
            isInterrupted = true;
        }
    }

    //Puts pill into queue when main body is done
    try {
        store.put(END_FLAG);
    } catch (InterruptedException ex) {
        System.err.printf("Error setting flag in store.%nWordReverser%n%s%n", ex);
    }
}

My run() method from the Writer Class: 我的Writer类中的run()方法:

    /**
     * Executes when being called in a thread
     */
    @Override
    public void run() {
        boolean isInterrupted = false;

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(output))) {

            //Continue writing until the thread is interrupted
            while (!isInterrupted) {
                CharSequence word = in.take();

                if (word.toString().equalsIgnoreCase(END_FLAG)) {
                    isInterrupted = true;
                } else {
                    writer.write(word + " ");
                }
            }
        } catch (InterruptedException | IOException ex) {
            System.err.printf("Error writing to output file!%n%s%n", ex);
        }
    }
}

You're code is already halfway there ... 您的代码已经在其中了...

For the threads that block in the take() method, you need to: 对于在take()方法中阻塞的线程,您需要:

  1. Set the isRunning flag to false . isRunning标志设置为false
  2. Call Thread.interrupt() on the thread object. 在线程对象上调用Thread.interrupt()

And you could avoid exposing the isRunning flag by setting it to false in the handlers for the InterruptedException . 并且可以通过在InterruptedException的处理程序中将isRunning标志设置为false来避免暴露它。

For the thread that is blocking while reading from the Scanner , a Thread.interrupt() should result in a InterruptedIOException . 对于从Scanner读取时正在阻塞的线程, Thread.interrupt()应该导致InterruptedIOException If not, then calling close() on the stream that the Scanner is using should be sufficient. 如果没有,那么在Scanner正在使用的流上调用close()应该足够了。 It requires a little bit of reorganization; 它需要一点重组。 ie constructing the Scanner with a Reader or InputStream instead of a File object. 即使用ReaderInputStream而不是File对象构造Scanner

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM