如何进行线程交互?

时间:2016-06-09 18:41:17

标签: java multithreading wait notify

我想使用多线程(低级线程),但我遇到了一个问题。问题是因为至少wait方法将被一个线程调用而notifyAll将被另一个线程调用,问题是任何时候我运行程序,在我看来notifyAll在等待之前被调用,所以我"将永远等待"。

我的代码如下:

public class Reader extends Thread {

    Calculator c;

    public Reader(Calculator cal) {
        c=cal;
    }

    public void run (){
        synchronized(c) {
            try {
                System.out.println("Waiting for calculation");
                c.wait();
            } catch (InterruptedException ex) {}
            System.out.println("Total is:" +c.total);      
        }       
    }  

    public static void main (String[] a) {

        Calculator calculator=new Calculator();

        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
    }
}

class Calculator implements Runnable {

    int total;

    public void run()  {
        synchronized(this) {    
            for(int i=0;i<100;i++) {
                total=total+i;
            }       
            notifyAll();    
        }
    }
}

我在这里输出的是连续5次等待计算,所以我从未达到声明&#34;总数是总数。

我试图弄清楚如何解决问题,但仍未找到解决方案。如果有人有任何线索该做什么我将非常感激。

提前致谢

3 个答案:

答案 0 :(得分:1)

您实际上并没有执行计算器可运行的代码,您拥有的代码不会计算总数或通知任何内容。使用计算器启动一个新线程:

new Thread(calculator).start();

在您初始化计算器局部变量的行之后。这将使计算器在主线程启动读者时开始工作。

可能计算器可能在读者开始等待之前完成,读者最终会等待永远不会发出的通知。创建一个条件变量,在这种情况下,向Calculator添加一个布尔标志,初始化为false,一旦计算完成就设置为true(通过notifyAll,在离开synchronized块之前)。让读者等待循环:

synchronized(c) {
    try {
        System.out.println("Waiting for calculation");
        while (!c.finished) {
            c.wait();
        }
    } catch (InterruptedException ex) {}
    System.out.println("Total is:" +c.total);
}

这样,如果计算器在读者开始之前完成,读者将从条件变量中判断出计算器已完成且不会等待。

总是在循环中调用wait方法是个好习惯,因为

a)当线程接收到没有锁的通知时,在通知线程和重新获取锁之间的时间内,由于其他线程的操作,系统状态可能会发生变化(不是本例中的情况)和

b)你不能依赖终止等待意味着线程收到通知。

通过以上更改,程序正常完成此输出:

Waiting for calculation
Total is:4950
Waiting for calculation
Total is:4950
Waiting for calculation
Total is:4950
Waiting for calculation
Total is:4950
Waiting for calculation
Total is:4950

以下是完整的修订代码:

public class Reader extends Thread {

    Calculator c;

    public Reader(Calculator cal) {
        c=cal;
    }

    public void run (){
        synchronized(c) {
            try {
                System.out.println("Waiting for calculation");
                while (!c.finished) {
                    c.wait();
                }
            } catch (InterruptedException ex) {}
            System.out.println("Total is:" +c.total);      
        }       
    }  

    public static void main (String[] a) {

        Calculator calculator=new Calculator();
        new Thread(calculator).start();

        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
    }
}

class Calculator implements Runnable {

    int total;
    boolean finished;

    public void run()  {
        synchronized(this) {    
            finished = false;
            for(int i=0;i<100;i++) {
                total=total+i;
            }       
            notifyAll();    
            finished = true;
        }
    }
} 

答案 1 :(得分:0)

我建议澄清一下您对最终结果的期望。显然500?不确定:P

无论如何,我建议使用Java Executors。它们是以有组织的方式处理执行线程的默认Java类。试试这个:

public class Example {
    public static class Calculator implements Runnable {
        private int result = 0;

        public void run() {
            for (int i = 0; i < 100; i++) {
                result++;
            }

            System.out.println("Done. Result = " + result);
        }

        public int getResult() {
            return result;
        }
    }

    public static void main(final String[] args) {
        final Calculator c = new Calculator();
        final ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.submit(c);
        executorService.submit(c);
        executorService.submit(c);
        executorService.submit(c);
        executorService.submit(c);
    }
}

这给我以下内容:

Done. Result = 100
Done. Result = 200
Done. Result = 300
Done. Result = 400
Done. Result = 500

这段代码的作用是创建一个单线程执行器,它可以运行任务(Runnable个对象)并为此使用单个线程。因为它使用单个线程,所以每个任务必须等待它之前的任务才能启动。

如果你创建了一个多线程执行器,它将尝试在可用的线程中运行任务,并且所有线程都遵循与单线程一样的原则:对于每个线程,任务必须等待一个线程在它开始之前。但正如名称所说,使用多线程执行程序,有多个线程,这意味着任务将同时运行,并且现在将显示相同的行为。

希望这有帮助!

答案 2 :(得分:0)

尽管你写过关于低级线程的文章,但很清楚你的问题可以通过ForkJoinPool来解决。关键是你可以使用wait - notify来电轻松搞错;但是&#34;发生在&#34;可以使用wait方法而不是调用join来设置关系。

完整代码:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Reader extends Thread {

    final Future<Integer> future;

    public Reader(Future<Integer> future) {
        this.future = future;
    }

    @Override
    public void run() {
        try {
            int result = future.get();
            System.out.println("Total is:" + result);
        } catch (InterruptedException | ExecutionException ex) {
            Logger.getLogger(Reader.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] a) {
        Calculator calculator = new Calculator();
        ForkJoinPool pool = ForkJoinPool.commonPool();
        ForkJoinTask calcTask = pool.submit(calculator);
        ForkJoinTask[] tasks = {
            calcTask,
            pool.submit(new Reader(calcTask)),
            pool.submit(new Reader(calcTask)),
            pool.submit(new Reader(calcTask)),
            pool.submit(new Reader(calcTask)),
            pool.submit(new Reader(calcTask))
        };
        //do something else
        for (ForkJoinTask task : tasks) {
            task.join();
        }
    }
}

class Calculator implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int total = 0;
        for (int i = 0; i < 100; i++) {
            total = total + i;
        }
        return total;
    }
}