как установить логический флаг потока-1 из потока-2 в многопоточности Java

Я пишу простое приложение multithreaded, которое включает три потока: Thread-1, Thread-2 и main.

Thread-1 — это класс генератора random number, который производит random doubles и передает его Thread-2.

Thread-2 потребляет числа для расчета среднего. Я использовал PipedOutputStream, который Thread-1 передает с random numbers. Thread-2 использует PipedInputStream, чтобы съесть random numbers.

Вопрос в том::

если среднее значение превышает 1E5 в Thread-2, я хочу подать сигнал Thread-1, чтобы он прекратил производить числа. У меня есть boolean flag в Thread-1, который нужно включить. Как я могу этого добиться?

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.Random;

//

class NumGen extends Thread {

    PipedOutputStream pos;
    DataOutputStream dos;
    boolean isDone=false;

    public NumGen(PipedOutputStream pos){
        this.pos=pos;
        dos=new DataOutputStream(pos);
    }

    public void run(){
        while (!isDone){
            Random rand = new Random();
            try {
                dos.writeDouble(rand.nextDouble()+100.0);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

class RunningAvg extends Thread {

    PipedInputStream pis;
    DataInputStream dis;
    Double avg;
    int count;
    Double runningTotal;

    public RunningAvg(PipedInputStream pis){
        this.pis=pis;
        dis=new DataInputStream(pis);
            runningTotal=0.0;
    avg=0.0;
    }

    public void run(){
        try {
        while (dis.available()>0){
            count+=1;
            runningTotal+=dis.readDouble();
                avg=runningTotal/count;
                System.out.printf("The average in count no : %s is %s%n",count,avg);
                //if avg>1E5
                 //set NumGen.isDone=true
        }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }


public class InterThreadComm {

    public static void main(String[] args){


    try {
        PipedOutputStream pos= new PipedOutputStream();
        PipedInputStream pis = new PipedInputStream(pos);
        NumGen ng = new NumGen(pos);
        RunningAvg ra = new RunningAvg(pis);
        ng.start();
        ra.start();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    }


}

PS: код работает вечно, ничего не печатая на консоли, и я еще не понял, почему!!


person brain storm    schedule 04.03.2014    source источник
comment
Я думаю, вы изобретаете колесо. Для каждого потока java уже имеет внутренний флаг прерывания. Вам просто нужно вызвать метод interrupt() в первом потоке и правильно обработать прерывание.   -  person ferrerverck    schedule 04.03.2014
comment
@ferrerverck: я знаю о флаге прерывания. Мне нужно прервать thread-1 из thread-2. пожалуйста, покажите, как этого добиться?   -  person brain storm    schedule 04.03.2014


Ответы (4)


Вы можете использовать AtomicBoolean и передать его обоим потокам, атомарные типы доступны для многопоточности, а также потокобезопасны.

сначала объявите, что вы помечаете isDone как AtomicBoolean

private AtomicBoolean isDone;

затем объявите один объект AtomicBoolean и передайте его обоим потокам

PipedOutputStream pos= new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
AtomicBoolean isDone = new AtomicBoolean();
NumGen ng = new NumGen(pos, isDone);
RunningAvg ra = new RunningAvg(pis, isDone);
ng.start();
ra.start();

наконец, если вы хотите прекратить генерировать числа, попросите Thread-2 установить isDone false.

person Salah    schedule 04.03.2014

Я бы сделал так, чтобы процедура main() создавала новый объект AtomicBoolean, и я бы передал ссылки на объект конструкторам обоих классов Thread. Метод RunningAvg.run() может установить значение AtomicBoolean, а метод NumGen.run() может его проверить.

class NumGen extends Thread {
    ...
    AtomicBoolean isDone;

    public NumGen(PipedOutputStream pos, AtomicBoolean isDone){
        ...
        this.isDone = isDone;
    }

    public void run(){
        while (!isDone.get()){
            ...
        }
    }
}

class RunningAvg extends Thread {
    ...
    AtomicBoolean isDone;

    public RunningAvg(PipedInputStream pis, AtomicBoolean isDone){
        ...
        this.isDone = isDone;
    }

    public void run(){
        try {
        while (dis.available()>0){
            ...
            if (avg > 1E5) {
                isDone.set(true);
                ...
            }
        }
        ...
    }


public class InterThreadComm {

    public static void main(String[] args){

    try {
        ...
        AtomicBoolean isDone = new AtomicBoolean(false);
        NumGen ng = new NumGen(pos, isDone);
        RunningAvg ra = new RunningAvg(pis, isDone);
        ...
    }
person Solomon Slow    schedule 04.03.2014

Вам нужно создать метод, доступный в обоих случаях, используя sychronized.

например:

   public synchronized boolean getDone()
   {
            return isDone;
   }

В противном случае у вас возникнут проблемы с отслеживанием переменной между потоками.

Вот ссылка, которая должна помочь: http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

person David MacNeil    schedule 04.03.2014
comment
isDone флаг находится в Thread-1 как Thread-2 имеет к нему доступ - person brain storm; 04.03.2014
comment
Вы должны создать этот метод в потоке 1, а поток 2 должен только ждать, пока он станет истинным. синхронизированный сообщает другому потоку ожидать, потому что данные в настоящее время доступны или изменены другим потоком; как указано в статье, вы также можете делать синхронизированные операторы, но это не рекомендуется. - person David MacNeil; 04.03.2014

Я бы создал метод setIsDone() в NumGen.

public void setIsDone(boolean isDone) {
    this.isDone = isDone;
}

Ваш другой поток может вызвать это, когда придет время для завершения работы NumGen.

Кроме того, общая рекомендация заключается в том, что в большинстве случаев вам следует реализовать интерфейс Runnable вместо прямого создания подкласса Thread, но это просто эмпирическое правило, а не жесткое правило.

person Paul J Abernathy    schedule 04.03.2014
comment
Пожалуйста, объясните, как другой поток будет вызывать этот метод? - person brain storm; 04.03.2014
comment
Технически сам поток на самом деле не вызывает метод. Объект RunningAvg, который выполняется в Thread-2, будет вызывать его. RunningAvg будет иметь указатель на ваш объект NumGen (переданный в конструкторе или сеттере), и он вызовет numGen.setIsDone(true) внутри цикла while в своем методе run(). - person Paul J Abernathy; 04.03.2014