在java中以循环方式运行线程

时间:2013-08-19 13:20:07

标签: java multithreading synchronization

我是Java中多线程和同步的新手。我正在尝试实现一个任务,其中我给了5个文件,每个文件将被一个特定的线程读取。每个线程都应该从文件中读取一行,然后将执行转发到下一个线程,依此类推。当所有5个线程都读取第一行时,再次从线程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 
}}

需要帮助

2 个答案:

答案 0 :(得分:3)

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

  1. 当前线程无法向前移动以读取文件中的行,除非它之前的线程完成,因为它们应该以循环方式读取。
  2. 当前线程完成后读取该行,它必须通知另一个线程,否则该线程将永远等待。
  3. 我已经使用temp包中的一些文件测试了这段代码,并且能够以循环方式读取这些行。我相信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的构造函数获取每个文件中要读取的文件数和行数。还需要考虑边界条件。

答案 1 :(得分:2)

你错过了很多部分:

  • 您尝试在每个线程的本地对象上进行同步。这可能没有任何效果,JVM甚至可以删除整个锁定操作;

  • 执行notifyAll时没有匹配的wait;

  • 缺少的wait必须位于run方法的顶部,而不是您指明的底部。

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