繁体   English   中英

使用wait / notifyAll是否正确同步Java线程?

[英]Proper synchronization of Java threads using wait/notifyAll?

这是我的应用程序的简化版本,显示了我在做什么。

/*
in my app's main():

    Runner run = new Runner();

    run.dowork();

*/

class Runner
{
    private int totalWorkers = 2;
    private int workersDone = 0;

    public synchronized void workerDone()
    {
        workersDone++;
        notifyAll();
    }

    public synchronized void dowork()
    {
        workersDone = 0;

        //<code for opening a file here, other setup here, etc>

        Worker a = new Worker(this);
        Worker b = new Worker(this);

        while ((line = reader.readLine()) != null)
        {
            //<a large amount of processing on 'line'>

            a.setData(line);
            b.setData(line);

            while (workersDone < totalWorkers)
            {
                wait();
            }               
        }
    }
}

class Worker implements Runnable
{
    private Runner runner;
    private String data;

    public Worker(Runner r)
    {
        this.runner = r;
        Thread t = new Thread(this);
        t.start();
    }

    public synchronized void setData(String s)
    {
        this.data = s;
        notifyAll();
    }

    public void run
    {
        while (true)
        {
            synchronized(this)
            {
                wait();

                //<do work with this.data here>

                this.runner.workerDone();
            }
        }
    }
}

这里的基本概念是,我有一堆工人,他们都对传入的数据行进行全部独立的处理,并在需要的地方写出数据-他们不需要将任何数据报告回主线程或彼此共享数据。

我遇到的问题是此代码死锁。 我正在读取超过100万行的文件,很幸运在我的应用停止响应之前将100行插入其中。

实际上,这些工人的工作量是不同的,因此我想等到他们全部完成后再转到下一行。

我不能让工作人员以不同的速度处理数据并在内部将数据排队,因为我正在处理的文件太大,无法容纳在内存中。

我不能给每个工作人员自己的FileReader来独立获取“行”,因为我在工作人员看到该行之前对其进行了大量处理,并且不想在每个工作人员中都重新进行处理。

我知道我在Java中缺少一些相当简单的同步方面,但是我仍然停留在这一点上。 如果有人可以在这里解释我在做什么错,我将不胜感激。 我相信我误解了同步的某些方面,但是出于尝试修复同步的想法。

直接使用synchronizedwait()notify()肯定很棘手。

幸运的是, Java Concurrency API为此类事情提供了一些非常直观的出色控制对象。 特别要看一下CyclicBarrierCountDownLatch 几乎可以肯定其中之一就是您要寻找的东西。

您可能还会发现ThreadPoolExecutor在这种情况下很方便。

这是您的代码段的一个简单示例/转换,它产生以下输出(当然没有死锁):

读取行:1行
等待工作完成:1号线
在线工作:1号线
在线工作:1号线
读取行:第2行
等待工作完成:2号线
在线工作:2号线
在线工作:2号线
读线:第3行
等待工作完成:3号线
在线工作:3号线
在线工作:3号线
所有工作完成!

public class Runner
{

    public static void main(String args[]) {
        Runner r = new Runner();
        try {
            r.dowork();
        } catch (IOException e) {
            // handle
            e.printStackTrace();
        }
    }

    CyclicBarrier barrier;
    ExecutorService executor;
    private int totalWorkers = 2;

    public Runner() {
        this.barrier = new CyclicBarrier(this.totalWorkers + 1);
        this.executor = Executors.newFixedThreadPool(this.totalWorkers);
    }

    public synchronized void dowork() throws IOException
    {
        //<code for opening a file here, other setup here, etc>
        //BufferedReader reader = null;
        //String line;

        final Worker worker = new Worker();

        for(String line : new String[]{"Line 1", "Line 2", "Line 3"})
        //while ((line = reader.readLine()) != null)
        {
            System.out.println("Read line: " + line);
            //<a large amount of processing on 'line'>

            for(int c = 0; c < this.totalWorkers; c++) {
                final String curLine = line;
                this.executor.submit(new Runnable() {
                    public void run() {
                        worker.doWork(curLine);
                    }
                });
            }

            try {
                System.out.println("Waiting for work to be complete on line: " + line);
                this.barrier.await();
            } catch (InterruptedException e) {
                // handle
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                // handle
                e.printStackTrace();
            }
        }

        System.out.println("All work complete!");
    }

    class Worker
    {
        public void doWork(String line)
        {
            //<do work with this.data here>
            System.out.println("Working on line: " + line);

            try {
                Runner.this.barrier.await();
            } catch (InterruptedException e) {
                // handle
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                // handle
                e.printStackTrace();
            }
        }
    }    
}

恕我直言,您未正确放置“ workersDone = 0”。

public synchronized void dowork()
        {
                // workersDone = 0;

                //<code for opening a file here, other setup here, etc>

                Worker a = new Worker(this);
                Worker b = new Worker(this);

                while ((line = reader.readLine()) != null)
                {
                        workersDone = 0;

                        //<a large amount of processing on 'line'>

                        a.setData(line);
                        b.setData(line);

                        while (workersDone < totalWorkers)
                        {
                                wait();
                        }                               
                }
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM