简体   繁体   English

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

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

Here is a simplified version of my application showing what I'm doing. 这是我的应用程序的简化版本,显示了我在做什么。

/*
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();
            }
        }
    }
}

The basic concept here is that I have a bunch of workers which all do some processing on an incoming line of data, all independently, and write out the data wherever they like - they do not need to report any data back to the main thread or share data with each other. 这里的基本概念是,我有一堆工人,他们都对传入的数据行进行全部独立的处理,并在需要的地方写出数据-他们不需要将任何数据报告回主线程或彼此共享数据。

The problem that I'm having is that this code deadlocks. 我遇到的问题是此代码死锁。 I'm reading a file of over 1 million lines and I'm lucky to get 100 lines into it before my app stops responding. 我正在读取超过100万行的文件,很幸运在我的应用停止响应之前将100行插入其中。

The workers, in reality, all do differing amounts of work so I want to wait until they all complete before moving to the next line. 实际上,这些工人的工作量是不同的,因此我想等到他们全部完成后再转到下一行。

I cannot let the workers process at different speeds and queue the data internally because the files I am processing are too large for this and won't fit in memory. 我不能让工作人员以不同的速度处理数据并在内部将数据排队,因为我正在处理的文件太大,无法容纳在内存中。

I cannot give each worker its own FileReader to independently get 'line', because I do a ton of processing on the line before the workers see it, and do not want to have to re-do the processing in each worker. 我不能给每个工作人员自己的FileReader来独立获取“行”,因为我在工作人员看到该行之前对其进行了大量处理,并且不想在每个工作人员中都重新进行处理。

I know I'm missing some fairly simple aspect of synchronization in Java but I'm stuck at this point. 我知道我在Java中缺少一些相当简单的同步方面,但是我仍然停留在这一点上。 If someone could explain what I'm doing wrong here I would appreciate it. 如果有人可以在这里解释我在做什么错,我将不胜感激。 I believe I'm misunderstanding some aspect of the synchronization but I'm out of ideas for attempting to fix it. 我相信我误解了同步的某些方面,但是出于尝试修复同步的想法。

Working directly with synchronized , wait() , and notify() is definitely tricky. 直接使用synchronizedwait()notify()肯定很棘手。

Fortunately the Java Concurrency API provides some excellent control objects for this sort of thing that are much more intuitive. 幸运的是, Java Concurrency API为此类事情提供了一些非常直观的出色控制对象。 In particular, look at CyclicBarrier and CountDownLatch ; 特别要看一下CyclicBarrierCountDownLatch one of them almost certainly will be what you're looking for. 几乎可以肯定其中之一就是您要寻找的东西。

You may also find a ThreadPoolExecutor to be handy for this situation. 您可能还会发现ThreadPoolExecutor在这种情况下很方便。

Here's a simple example / conversion of your snippet that produces the following output (without deadlock, of course): 这是您的代码段的一个简单示例/转换,它产生以下输出(当然没有死锁):

Read line: Line 1 读取行:1行
Waiting for work to be complete on line: Line 1 等待工作完成:1号线
Working on line: Line 1 在线工作:1号线
Working on line: Line 1 在线工作:1号线
Read line: Line 2 读取行:第2行
Waiting for work to be complete on line: Line 2 等待工作完成:2号线
Working on line: Line 2 在线工作:2号线
Working on line: Line 2 在线工作:2号线
Read line: Line 3 读线:第3行
Waiting for work to be complete on line: Line 3 等待工作完成:3号线
Working on line: Line 3 在线工作:3号线
Working on line: Line 3 在线工作:3号线
All work complete! 所有工作完成!

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();
            }
        }
    }    
}

IMHO you have improperly placed "workersDone = 0". 恕我直言,您未正确放置“ 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