如何用队列组织多线程工作?

时间:2013-02-19 10:53:44

标签: java multithreading find queue synchronized

我很开心,让自己保持警惕。

我需要在文件中组织多线程查找:

用户输入find(路径)和find(word);

  • 第一个线程在文件夹中找到.txt个文件并将结果添加到队列中;
  • 当队列有一些文件=>第二个线程开始在此文件中查找 什么需要找(字)。
  • 如果找到成功就会显示出这条道路 文件+这个词在文件中遇到的次数。

问题:

  • 我们可以使用ArrayList(或存在任何替代方案)用于几个线程的队列吗?
  • 如果队列为空怎么办,第二个线程没有启动但是等待第一个找到需要文件的时候?
  • 我们是否需要使用synchronized进行此任务并继承MultiThreadingSearch(或更好地使用合成)?

代码:

import java.util.*;
import java.io.*;

class ArrayListOfFiles {
    private Node first, last;

    private class Node {
        String item;
        Node next;
    }

    public boolean isEmpty() {
        return first == null;
    }

    public synchronized void enqueue(String item) {
        Node oldlast = last;
        last = new Node();
        last.item = item;
        last.next = null;
        if (isEmpty())
            first = last;
        else
            oldlast.next = last;
    }

    public synchronized String dequeue() {
        String item = first.item;
        first = first.next;
        if (isEmpty())
            last = null;
        return item;
    }
}

class FolderScan extends MultiThreadingSearch implements Runnable {

    FolderScan(String path, String whatFind) {
        super(path, whatFind);
    }

    @Override
    public void run() {
        findFiles(path);
    }

    ArrayListOfFiles findFiles(String path) {
        File root = new File(path);
        File[] list = root.listFiles();
        for (File titleName : list) {
            if (titleName.isDirectory()) {
                findFiles(titleName.getAbsolutePath());
            } else {
                if (titleName.getName().toLowerCase().endsWith((".txt"))) {
                    textFiles.enqueue(titleName.getName());
                }
            }
        }

        return textFiles;
    }

}

class FileScan extends MultiThreadingSearch implements Runnable {
    Scanner scanner = new Scanner((Readable) textFiles);
    Set<String> words = new HashSet<String>();
    int matches = 0;

    FileScan(String file, String whatFind) {
        super(file, whatFind);
        Thread wordFind = new Thread();
        wordFind.start();
    }

    @Override
    public void run() {
        while (scanner.hasNext()) {
            String word = scanner.next();
            words.add(word);
        }

        if (words.contains(this.whatFind)) {
            System.out.println("File:" + this.path);
            matches++;
        }

        System.out.println(matches);
    }

}

public class MultiThreadingSearch {
    String path;
    String whatFind;

    ArrayListOfFiles textFiles;

    MultiThreadingSearch(String path, String whatFind) {
        this.path = path;
        this.whatFind = whatFind;
        this.textFiles = new ArrayListOfFiles();

        Thread pathFind = new Thread(new FolderScan(path, whatFind));
//      pathFind.start();

        if (!textFiles.isEmpty()) {
            @SuppressWarnings("unused")
            FileScan fileScan = new FileScan(textFiles.dequeue(), whatFind);
        }

    }

    // ask user about input
    public static void askUserPathAndWord() {

        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(System.in));
        String path;
        String whatFind;
        try {
            System.out.println("Please, enter a Path and Word"
                    + "(which you want to find):");
            System.out.println("Please enter a Path:");
            path = bufferedReader.readLine();
            System.out.println("Please enter a Word:");
            whatFind = bufferedReader.readLine();

            if (path != null && whatFind != null) {
                new MultiThreadingSearch(path, whatFind);
                System.out.println("Thank you!");
            } else {
                System.out.println("You did not enter anything");
            }

        } catch (IOException | RuntimeException e) {
            System.out.println("Wrong input!");
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        askUserPathAndWord();
    }
}

我从这段代码中得到Exception in thread "main" java.lang.StackOverflowError 怎么能解决这个任务?

谢谢,
纳扎尔。

3 个答案:

答案 0 :(得分:5)

检查BlockingQueue它完全符合您的需要。线程可以阻塞,直到某个其他线程将新项添加到队列中 至于如何分解你的系统。我会做以下事情:

  • 创建用于在路径中搜索txt文件的类。它实现了Runnable。您将pathqueue传递给它。它会搜索txt文件的路径并将它们添加到队列中。
  • 创建用于搜索文件内容的类。它实现了Runnable。您将whatFindqueue传递给它,它从队列中获取新文件并检查其内容。

类似的东西:

BlockingQueue<File> queue = new LinkedBlockingQueue<File>();
String path = ...;
String whatFind = ...;
FolderScan folderScan = new FolderScan(path, queue);
FileScan fileScan = new FileScan(whatFind, queue);

Executor executor = Executors.newCachecThreadPool();
executor.execute(folderScan);
executor.execute(fileScan);

如果您希望FileScan等到FolderScan向队列中添加内容,您可以使用take方法:

BlockingQueue<File> queue;
File toProcess = queue.take(); // this line blocks current thread (FileScan) until someone adds new item to the queue.

答案 1 :(得分:0)

更改后:

package task;

import java.util.concurrent.*;
import java.util.*;
import java.io.*;

class FolderScan implements Runnable {

    private String path;
    private BlockingQueue<File> queue;
    private CountDownLatch latch;
    private File endOfWorkFile;

    FolderScan(String path, BlockingQueue<File> queue, CountDownLatch latch,
            File endOfWorkFile) {
        this.path = path;
        this.queue = queue;
        this.latch = latch;
        this.endOfWorkFile = endOfWorkFile;
    }

    public FolderScan() {  }

    @Override
    public void run() {
        findFiles(path);
        queue.add(endOfWorkFile);
        latch.countDown();
    }

    private void findFiles(String path) {

        try {
            File root = new File(path);
            File[] list = root.listFiles();
            for (File currentFile : list) {
                if (currentFile.isDirectory()) {
                    findFiles(currentFile.getAbsolutePath());
                } else {
                    if (currentFile.getName().toLowerCase().endsWith((".txt"))) {
                            queue.put(currentFile);
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

public class FileScan implements Runnable {

    private String whatFind;
    private BlockingQueue<File> queue;
    private CountDownLatch latch;
    private File endOfWorkFile;

    public FileScan(String whatFind, BlockingQueue<File> queue,
            CountDownLatch latch, File endOfWorkFile) {
        this.whatFind = whatFind;
        this.queue = queue;
        this.latch = latch;
        this.endOfWorkFile = endOfWorkFile;
    }

    public FileScan() {     }

    Set<String> words = new HashSet<String>();
    int matches = 0;

    @Override
    public void run() {

        while (true) {
            try {
                File file;
                file = queue.take();

                if (file == endOfWorkFile) {
                    break;
                }

                scan(file);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        latch.countDown();
    }

    private void scan(File file) {
        Scanner scanner = null;
        try {
            scanner = new Scanner(file);
        } catch (FileNotFoundException e) {
            System.out.println("FileNotFoundException.");
            e.printStackTrace();
        }
        while (scanner.hasNext()) {
            String word = scanner.next();
            words.add(word);
        }

        if (words.contains(this.whatFind)) {
            matches++;
        }

        String myStr = String.format("File: %s and the number of matches "
                + "is = %d", file.getAbsolutePath(), matches);
        System.out.println(myStr);

        matches = 0;
    }

    // ask user about input
    public void askUserPathAndWord() {

        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(System.in));
        String path;
        String whatFind;
        BlockingQueue<File> queue = new LinkedBlockingQueue<File>();

        try {
            System.out.println("Please, enter a Path and Word"
                    + "(which you want to find):");
            System.out.println("Please enter a Path:");
            path = bufferedReader.readLine();
            System.out.println("Please enter a Word:");
            whatFind = bufferedReader.readLine();

            if (path != null && whatFind != null) {

                File endOfWorkFile = new File("GameOver.tmp");
                CountDownLatch latch = new CountDownLatch(2);

                FolderScan folderScan = new FolderScan(path, queue, latch,
                        endOfWorkFile);
                FileScan fileScan = new FileScan(whatFind, queue, latch,
                        endOfWorkFile);

                Executor executor = Executors.newCachedThreadPool();
                executor.execute(folderScan);
                executor.execute(fileScan);

                latch.await();
                System.out.println("Thank you!");
            } else {
                System.out.println("You did not enter anything");
            }

        } catch (IOException | RuntimeException e) {
            System.out.println("Wrong input!");
            e.printStackTrace();
        } catch (InterruptedException e) {
            System.out.println("Interrupted.");
            e.printStackTrace();
        }
    }

    /**
     * @param args
     */

    public static void main(String[] args) {
        new FileScan().askUserPathAndWord();
    }
}

答案 2 :(得分:-1)

这可能听起来不太具有建设性,但您可以先修复该代码或先阅读this之类的内容,然后再丢弃代码。 Stackoverflow通常来自比预期更深的递归。确保递归方法中存在一些阻止递归的条件。