0

我是java中的多线程和同步的新手。我正在尝试完成一项任务,其中给我 5 个文件,每个文件将由一个特定线程读取。每个线程都应该从文件中读取一行,然后将执行转发到下一个线程,依此类推。当所有 5 个线程读取第一行时,再次从线程 1 运行第 1 行开始。文件 1 的 2 等等。

    Thread ReadThread1 = new Thread(new ReadFile(0));
    Thread ReadThread2 = new Thread(new ReadFile(1));
    Thread ReadThread3 = new Thread(new ReadFile(2));
    Thread ReadThread4 = new Thread(new ReadFile(3));
    Thread ReadThread5 = new Thread(new ReadFile(4));

    // starting all the threads
    ReadThread1.start();
    ReadThread2.start();
    ReadThread3.start();
    ReadThread4.start();
    ReadThread5.start();

在 ReadFile (实现 Runnable,在 run 方法中,我试图在 bufferreader 对象上同步。

        BufferedReader br = null;

            String sCurrentLine;
            String filename="Source/"+files[fileno];
            br = new BufferedReader(new FileReader(filename));

            synchronized(br)
            {

            while ((sCurrentLine = br.readLine()) != null) {
                int f=fileno+1;
                System.out.print("File No."+f);
                System.out.println("-->"+sCurrentLine);
br.notifyAll();
// some thing needs to be dine here i guess 
}}

需要帮忙

4

2 回答 2

3

虽然这不是使用多线程的理想方案,但由于这是分配,我提出了一个可行的解决方案。线程将按顺序执行,有几点需要注意:

  1. 当前线程不能继续读取文件中的行,除非它的前一个线程完成,因为它们应该以循环方式读取。
  2. 当前线程完成读取该行后,它必须通知另一个线程,否则该线程将永远等待。

我已经用临时包中的一些文件测试了这段代码,它能够以循环方式读取这些行。我相信Phaser也可以用来解决这个问题。

    public class FileReaderRoundRobinNew {
        public Object[] locks;

        private static class LinePrinterJob implements Runnable {
            private final Object currentLock;


private final Object nextLock;
        BufferedReader bufferedReader = null;

        public LinePrinterJob(String fileToRead, Object currentLock, Object nextLock) {
            this.currentLock = currentLock;
            this.nextLock = nextLock;
            try {
                this.bufferedReader = new BufferedReader(new FileReader(fileToRead));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            /*
             * Few points to be noted:
             * 1. Current thread cannot move ahead to read the line in the file until and unless its immediately previous thread is done as they are supposed to read in round-robin fashion.
             * 2. After current thread is done reading the line it must notify the other thread else that thread will wait forever.
             * */
            String currentLine;
            synchronized(currentLock) {
                try {
                    while ( (currentLine = bufferedReader.readLine()) != null) {
                        try {
                            currentLock.wait();
                            System.out.println(currentLine);
                        }
                        catch(InterruptedException e) {}
                        synchronized(nextLock) {
                            nextLock.notify();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            synchronized(nextLock) {
                nextLock.notify(); /// Ensures all threads exit at the end
            }
        }
    }

    public FileReaderRoundRobinNew(int numberOfFilesToRead) {
        locks = new Object[numberOfFilesToRead];
        int i;
        String fileLocation = "src/temp/";
        //Initialize lock instances in array.
        for(i = 0; i < numberOfFilesToRead; ++i) locks[i] = new Object();
        //Create threads
        int j;
        for(j=0; j<(numberOfFilesToRead-1); j++ ){
            Thread linePrinterThread = new Thread(new LinePrinterJob(fileLocation + "Temp" + j,locks[j],locks[j+1]));
            linePrinterThread.start();
        }
        Thread lastLinePrinterThread = new Thread(new LinePrinterJob(fileLocation + "Temp" + j,locks[numberOfFilesToRead-1],locks[0]));
        lastLinePrinterThread.start();
    }

    public void startPrinting() {
        synchronized (locks[0]) {
            locks[0].notify();
        }
    }

    public static void main(String[] args) {
        FileReaderRoundRobinNew fileReaderRoundRobin = new FileReaderRoundRobinNew(4);
        fileReaderRoundRobin.startPrinting();
    }
}

如果唯一的目标是以循环方式读取文件而不是严格按照相同的顺序,那么我们也可以使用 Phaser。在这种情况下,读取文件的顺序并不总是相同的,例如,如果我们有四个文件(F1、F2、F3 和 F4),那么在第一阶段它可以将它们读取为 F1-F2-F3-F4 但在下一个阶段它可以将它们读取为 F2-F1-F4-F3。为了完成,我仍然提出这个解决方案。

public class FileReaderRoundRobinUsingPhaser {

    final List<Runnable> tasks = new ArrayList<>();
    final int numberOfLinesToRead;

    private static class LinePrinterJob implements Runnable {
        private BufferedReader bufferedReader;

        public LinePrinterJob(BufferedReader bufferedReader) {
            this.bufferedReader = bufferedReader;
        }

        @Override
        public void run() {
            String currentLine;
            try {
                currentLine = bufferedReader.readLine();
                System.out.println(currentLine);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public FileReaderRoundRobinUsingPhaser(int numberOfFilesToRead, int numberOfLinesToRead) {
        this.numberOfLinesToRead = numberOfLinesToRead;
        String fileLocation = "src/temp/";
        for(int j=0; j<(numberOfFilesToRead-1); j++ ){
            try {
                tasks.add(new LinePrinterJob(new BufferedReader(new FileReader(fileLocation + "Temp" + j))));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    public void startPrinting( ) {
        final Phaser phaser = new Phaser(1){
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("Phase Number: " + phase +" Registeres parties: " + getRegisteredParties() + " Arrived: " + getArrivedParties());
                return ( phase >= numberOfLinesToRead || registeredParties == 0);
            }
        };

        for(Runnable task : tasks) {
            phaser.register();
            new Thread(() -> {
                do {
                    phaser.arriveAndAwaitAdvance();
                    task.run();
                } while(!phaser.isTerminated());
            }).start();
        }
        phaser.arriveAndDeregister();
    }

    public static void main(String[] args) {
        FileReaderRoundRobinUsingPhaser fileReaderRoundRobin = new FileReaderRoundRobinUsingPhaser(4, 4);
        fileReaderRoundRobin.startPrinting();
        // Files will be accessed in round robin fashion but not exactly in same order always. For example it can read 4 files as 1234 then 1342 or 1243 etc.
    }

}

上面的例子可以根据具体要求进行修改。此处 FileReaderRoundRobinUsingPhaser 的构造函数获取文件数和要从每个文件中读取的行数。还需要考虑边界条件。

于 2015-04-29T05:13:07.980 回答
2

你错过了这个难题的许多部分:

  • 您尝试在每个线程本地的对象上进行同步。这不会产生任何影响,JVM 甚至可能会移除整个锁定操作;

  • 您在notifyAll没有匹配的情况下执行wait

  • 缺少的wait必须在方法的顶部run而不是您指示的底部。

总而言之,恐怕此时修复您的代码超出了 StackOverflow 答案的范围。我的建议是首先熟悉核心概念:Java 中锁的语义,它们如何与waitand互操作,notify以及这些方法的精确语义。有关该主题的 Oracle 教程将是一个不错的开始。

于 2013-08-19T13:41:01.363 回答