Обмен статическими значениями между двумя потоками Java - PullRequest
0 голосов
/ 25 октября 2018

У меня есть два разных подпроцесса, выполняющих две разные команды в двух разных потоках. Второй поток обновляет значение в статической переменной, а первый поток принимает значение и использует его.

Поток должен быть таким: Thread2 обновляет статическую переменную, а thread1 получает значение из статической переменной, а затем печатает его ... но поток, который происходит, - thread1 сначала получает значение из статической переменной.В этом случае он имеет нулевое значение, а затем thread2 обновляет значение.

Оба потока работают параллельно, я использую класс ExecutorService для этого.Я использую класс Runtime для запуска команды и цикл while для непрерывного чтения выходных данных из потока в обоих потоках.

Thread1 продолжает давать значения (X, Y), второй поток выдает только значениекогда он получает текст.

Вывод, который я получаю:

(12, 123) null -> thread2 не получил никакого значения, поэтому он не обновляется, thread1 получит ноль изстатическая переменная
(123,334) null -> thread1 берет значение из статической переменной и использует его, thread2 затем обновляет значение «Hello» статической переменной
(134,654) «Hello» -> thread1 pickдо «Hello» и использует его, thread2 затем обновляет значение «World» до статической переменной

Ожидаемый результат:

(12, 123) null -> thread2 не полученлюбое значение, чтобы оно не обновлялось, thread1 получало бы нулевое значение из статической переменной
(123,334) «Hello» -> thread2 обновляет значение «Hello» статической переменной, thread1 выбирает его и использует
(134,654)"World" -> thread2 обновляет значение "World" до statпеременная ic, thread1 берет его и использует

Я также использовал переменную volatile, но вывод не изменился.Я что-то здесь упускаю?Пожалуйста, помогите ...

Ответы [ 2 ]

0 голосов
/ 26 октября 2018

Чего не следует делать при использовании потоков: запись и чтение одной и той же переменной без надлежащей синхронизации.Вы нарушаете это правило и поэтому сталкиваетесь с ожидаемыми проблемами с неопределенными результатами.

Представьте себе следующий поток выполнения:

T1: set var to "Hello"
T2: checks var and notices it is a String, proceeds to print it.
T1: set var to null
T2: prints var. (which is already null again)

Возможное решение состоит в том, что T2 сначала копирует значение в поток-local и затем продолжает работать над этим, iE:

while (running) {
  final String localString = staticString;
  if (localString != null) {
    System.out.println(localString);
  }
}

Таким образом, вы создаете «снимок» текущего значения переменной и фактически выводите то, что было снято в этот момент.Однако это по-прежнему недопустимый способ синхронизации между потоками, поскольку состояние моментального снимка является абсолютно случайным.Представьте, что происходит следующее:

T1: set var to "hello"
T1: set var to null
T2: checks var, it is null, do nothing.
T1: set var to "world"
T1: set var to null
T2: checks var, it is null, do nothing.

При работе с потоками необходимо помнить, что состояние любого потока в любое время всегда не определено, если вы не применяете его принудительно.

Существуют простые способы применения определенного состояния, например, iE synchronized, что в основном означает «Переведите все это в режим ожидания, чтобы я мог выполнять свою работу», однако это довольно неэффективно, поскольку нет смысла работатьпараллельно, если вы постоянно ставите все на удержание.

Намного лучший способ приблизиться к многопоточности - это с точки зрения архитектуры, т.е. с помощью триггеров.В вашем случае поток-писатель может иметь очередь только с соответствующей информацией (строки для вывода), а поток-производитель помещает строки в эту очередь:

T2: checks queue, it is empty, do nothing.
T1: t1.put("Hello")
T2: checks queue, it contains "Hello", print that.
T2: checks queue, it is empty, do nothing.
T2: checks queue, it is empty, do nothing.
T1: t1.put("World")
T2: checks queue, it contains "World", print that.

Iрекомендуем прочитать параллельный пакет , чтобы узнать об инструментах, предлагаемых Java.Почти все проблемы могут быть легко решены с помощью них.

0 голосов
/ 25 октября 2018

Вы можете использовать java.util.concurrent.Semaphore (начиная с Java 5), ​​чтобы контролировать количество потоков, одновременно обращающихся к общей переменной. Вы создаете экземпляр этого класса с количеством потоков (которые вызвали acqu ()) разрешено использовать то, что у них общего, и если вы передадите 0, это означает, что никто из них не сможет его изменить.

public class Threaad extends Thread{
volatile static String string;
public static void main(String[] args) {
    Semaphore semaphore = new Semaphore(0);
    Thread writer = new Write(semaphore);
    writer.setName("writer");
    Thread reader = new Read(semaphore);
    reader.setName("reader");
    ExecutorService service = Executors.newCachedThreadPool();
    service.execute(writer);
    service.execute(reader);
    service.shutdown();
}
}
class Write extends Thread{
    private Semaphore semaphore;
    Write(Semaphore semaphore){
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Threaad.string = String.valueOf(i);
            semaphore.release();
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Read extends Thread{
    private Semaphore semaphore;
    Read(Semaphore semaphore){
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                semaphore.acquire();
                System.out.println(Threaad.string);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...