我必须完成一个生产者/消费者程序的作业。我需要一个线程来读取文件,需要一个线程来使从第二个单词开始的每个其他单词都反向,并且需要一个线程将这些单词写到文件中。规则如下:
单词是任何字符序列,后跟空格。标点符号必须留在单词的结尾,即使颠倒了
您的程序必须使用三个线程,这些线程使用大小为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
起动器何时应关闭?
您还应该查看静态类成员,并尝试问自己为什么要使用它们。