我必须完成一个生产者/消费者程序的作业。我需要一个线程来读取文件,需要一个线程来使从第二个单词开始的每个其他单词都反向,并且需要一个线程将这些单词写到文件中。规则如下:


单词是任何字符序列,后跟空格。标点符号必须留在单词的结尾,即使颠倒了
您的程序必须使用三个线程,这些线程使用大小为2的阻塞队列进行通信
除了队列之外,线程无法彼此通信并且彼此之间没有引用
必须从JFileChooser对象获取输入和输出文件。输入需要一次读取一个单词,并将其通过阻塞队列传递给反向线程。处理线程需要从文件中取出所有其他单词并将其反转。所有线程都通过阻塞队列传递给输出。
阅读完成后,输入线程需要关闭文件
向单词写完后,输出需要关闭文件
当没有更多的单词要反向时,反向单词的线程将终止
所有线程必须通过耗尽工作或被中断来终止。不使用System.exit。


我已经完成了程序,但是仍然收到来自我的WordReverser类和WordWriter类的NullPointerException。



我将在下面列出我的代码。

它将按照顺序


主班
WordReader:从文件读取输入
WordReverser:从第二个单词开始反转所有其他单词
WordWriter:一次将一个单词写入输出文件


主类:


package ProducerConsumerAssignment;

import java.io.File;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

import javax.swing.JFileChooser;

/**
 * Test the producer consumer package
 *
 * @author Tyler Weaver
 */
public class Tester {

    public static void main(String[] args) {
        final int MAX_SIZE = 2;
        File input, output;
        JFileChooser chooser = new JFileChooser();
        BlockingQueue fromReader = new ArrayBlockingQueue(MAX_SIZE);
        BlockingQueue toWriter = new ArrayBlockingQueue(MAX_SIZE);
        ExecutorService service = Executors.newCachedThreadPool();
        int returnVal, exitVal;

        do {
            input = output = null;
            returnVal = chooser.showOpenDialog(null);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                input = chooser.getSelectedFile();
            }
        } while (returnVal != JFileChooser.APPROVE_OPTION);

        do {
            exitVal = chooser.showSaveDialog(null);
            if (exitVal == JFileChooser.APPROVE_OPTION) {
                output = chooser.getSelectedFile();
            }
        } while (exitVal != JFileChooser.APPROVE_OPTION);

        Runnable reader = new WordReader(input, fromReader);
        Runnable rev = new WordReverser(fromReader, toWriter);
        Runnable writer = new WordWriter(output, toWriter);

        service.execute(reader);
        service.execute(rev);
        service.execute(writer);
        service.shutdown();
    }
}



ReaderClass:

package ProducerConsumerAssignment;

import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader;

import java.util.concurrent.BlockingQueue; import java.util.Scanner;

/**  * Reads words from a file and places them into a blocking queue to be read  *  * @author Tyler Weaver  */ public class WordReader implements Runnable {

    //The blocking queue to store
    private static BlockingQueue<CharSequence> bin;
    private final File loc;             //File to read from

    /**
     * Constructor for WordReader
     *
     * @param input the text file to read from
     * @param bin the blocking queue to store the words
     */
    public WordReader(final File input, BlockingQueue bin) {
        loc = input;
        WordReader.bin = bin;
    }

    /**
     * Called when being executed Reads words from a file and places into a
     * blocking queue
     */
    @Override
    public void run() {

        try (Scanner in = new Scanner(new FileReader(loc))) {
            while (in.hasNext()) {
                bin.offer(in.next());
            }
        } catch (FileNotFoundException ex) {
            System.err.printf("Error finding File!%n%s%n", ex);
        }
    } }


反向器类:

package ProducerConsumerAssignment;

import java.util.concurrent.BlockingQueue;

/**
 * Takes a word from a blocking queue and reverses it. Puts the reversed word
 * into another blocking queue.
 *
 * @author Tyler Weaver
 */
public class WordReverser implements Runnable {

    private static BlockingQueue<CharSequence> intake, store;
    private static int oddWord;

    /**
     * Constructor for Word Reverser
     *
     * @param intake the blocking queue to retrieve words from
     * @param store the blocking queue to store the words
     */
    public WordReverser(BlockingQueue intake, BlockingQueue store) {
        WordReverser.intake = intake;
        WordReverser.store = store;
        oddWord = 0;
    }

    /**
     * Called when being executed. Reverses a word by taking from intake and
     * places the reversed word into store
     */
    @Override
    public void run() {
        StringBuilder str = new StringBuilder(intake.poll());
        if (oddWord % 2 == 1) {
            str = reverseWord(str);
        }

        store.offer(str);
        ++oddWord;
    }

    /**
     * Reverses a word, leaving behind punctuation if there is any
     *
     * @param word the word to reverse
     * @return a stringbuilder object containing the reversed word
     */
    private StringBuilder reverseWord(StringBuilder word) {
        char punct = Character.MAX_VALUE;

        //If has punctuation at the end, remove the punctuation
        if (!Character.isLetterOrDigit(word.charAt(word.length() - 1))) {
            punct = word.charAt(word.length() - 1);
            word.deleteCharAt(word.length() - 1);
        }

        word = word.reverse();

        if (punct == Character.MAX_VALUE) {
            return word;
        }

        return word.append(punct);
    }
}


作家班:

package ProducerConsumerAssignment;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import java.util.concurrent.BlockingQueue;

/**
 *
 * @author Tyler Weaver
 */
public class WordWriter implements Runnable {

    private static BlockingQueue<CharSequence> in;
    private final File output;

    /**
     * Constructs a WordWriter object
     *
     * @param file the file to write words to
     * @param queue the blocking queue to retrieve words from
     */
    public WordWriter(final File file, BlockingQueue queue) {
        output = file;
        in = queue;
    }

    /**
     * Executes when being called in a thread
     */
    @Override
    public void run() {
        try (BufferedWriter out = new BufferedWriter(new FileWriter(output))) {
            out.write(in.poll().toString() + " ");
        } catch (IOException ex) {
            System.err.printf("Error closing the file!%n%s%n", ex);
        }
    }
}




我相信问题出在我的BlockingQueues中,但我不确定。我们被告知,如果BlockingQueue中没有任何单词,则线程将阻塞并等待一个单词。但是似乎根本没有等待。任何帮助,将不胜感激。

编辑:抛出nullpointerexceptions的点是WordReverser的代码的第34行:StringBuilder str = new StringBuilder(intake.poll());

和WordWriter类的第36行的代码:

out.write(in.poll().toString() + " ");


这就是为什么我感到困惑。我们被告知,当试图从不存在的BlockingQueue中提取数据时,线程会阻塞自身。

最佳答案

好吧,我至少看到了1个问题。您在.poll()上使用BlockingQueue,该文档说:


  检索并删除此队列的头,或者返回{@code null}
  如果此队列为空。


因此,如果您的队列中没有任何项目,并且您从队列中进行轮询,但是现在还没有任何内容,则您的代码将在null项目上执行其余任务。

如果要在商品可用之前进行封锁,请使用take()


  检索并删除此队列的头,如有必要,请等待
  直到元素可用。


如文档所述,这将导致线程等待,直到某个项目可用。此操作还会引发InterruptedException,因为调用线程可以被中断。有多种方法可以阻止发布者/消费者使用其他药物(例如毒丸),但是我不会在这里讨论它们。

有多种方法可以解决此问题,但是我认为您需要回答以下问题并看一下您的设计:


您的WordReader何时完成发布数据?
WordReverser如何知道它的数据不足?
WordWriter如何知道没有更多数据可写?
您将如何在线程之间馈送数据?
您的ExecutorService起动器何时应关闭?


您还应该查看静态类成员,并尝试问自己为什么要使用它们。

07-26 09:29