Как заставить поток Java ждать выхода другого потока?

Я делаю приложение Java с потоком приложения-логики и потоком доступа к базе данных. Оба они сохраняются на протяжении всего срока службы приложения, и оба они должны работать одновременно (один разговаривает с сервером, один разговаривает с пользователем, когда приложение полностью запущено, мне нужно, чтобы оба они работали).

Однако при запуске мне нужно убедиться, что изначально поток приложения ожидает, пока поток db не будет готов (в настоящее время определяется опросом пользовательского метода dbthread.isReady()). Я бы не прочь, если бы потоки приложений блокировались до тех пор, пока поток db не был готов.

Thread.join() не похож на решение - поток db только закрывается при отключении приложения.

while (!dbthread.isReady()) {} вид работ, но пустая петля потребляет много процессорных циклов.

Любые другие идеи? Спасибо.

Ответ 1

Я бы порекомендовал вам пройти через учебник, например Sun Java Concurrency, прежде чем вы начнете в волшебном мире многопоточности.

Существует также ряд хороших книг (google для "Параллельное программирование в Java", "Java Concurrency на практике".

Чтобы получить ответ:

В вашем коде, который должен ждать dbThread, вы должны иметь что-то вроде этого:

//do some work
synchronized(objectYouNeedToLockOn){
    while (!dbThread.isReady()){
        objectYouNeedToLockOn.wait();
    }
}
//continue with work after dbThread is ready

В вашем методе dbThread вам нужно будет сделать что-то вроде этого:

//do db work
synchronized(objectYouNeedToLockOn){
    //set ready flag to true (so isReady returns true)
    ready = true;
    objectYouNeedToLockOn.notifyAll();
}
//end thread run method here

objectYouNeedToLockOn Я использую в этих примерах, предпочтительно, объект, который вам нужно манипулировать одновременно с каждым потоком, или вы можете создать отдельный Object для этой цели (я бы не рекомендовал синхронизировать сами методы ):

private final Object lock = new Object();
//now use lock in your synchronized blocks

Чтобы ваше понимание:
Существуют и другие (иногда лучшие) способы сделать это, например. с CountdownLatches и т.д. Начиная с Java 5 в пакете java.util.concurrent и подпакетах есть много отличных Concurrency классов. Вам действительно нужно найти материал в Интернете, чтобы узнать concurrency, или получить хорошую книгу.

Ответ 2

Используйте CountDownLatch с помощью счетчика 1.

CountDownLatch latch = new CountDownLatch(1);

Теперь в потоке приложения do -

latch.await();

В потоке db, после того, как вы закончите, do -

latch.countDown();

Ответ 3

Требование::

  • Ожидание выполнения следующего потока до тех пор, пока предыдущий не завершится.
  • Следующая нить не должна начинаться до тех пор, пока не прекратится предыдущий поток, независимо от времени.
  • Он должен быть простым и простым в использовании.

Ответ::

@See java.util.concurrent.Future.get() doc.

future.get() Ожидает, если необходимо для завершения вычисления, а затем извлекает его результат.

Работа выполнена! Пример ниже

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Test;

public class ThreadTest {

    public void print(String m) {
        System.out.println(m);
    }

    public class One implements Callable<Integer> {

        public Integer call() throws Exception {
            print("One...");
            Thread.sleep(6000);
            print("One!!");
            return 100;
        }
    }

    public class Two implements Callable<String> {

        public String call() throws Exception {
            print("Two...");
            Thread.sleep(1000);
            print("Two!!");
            return "Done";
        }
    }

    public class Three implements Callable<Boolean> {

        public Boolean call() throws Exception {
            print("Three...");
            Thread.sleep(2000);
            print("Three!!");
            return true;
        }
    }

    /**
     * @See java.util.concurrent.Future.get() doc
     *      <p>
     *      Waits if necessary for the computation to complete, and then
     *      retrieves its result.
     */
    @Test
    public void poolRun() throws InterruptedException, ExecutionException {
        int n = 3;
        // Build a fixed number of thread pool
        ExecutorService pool = Executors.newFixedThreadPool(n);
        // Wait until One finishes it task.
        pool.submit(new One()).get();
        // Wait until Two finishes it task.
        pool.submit(new Two()).get();
        // Wait until Three finishes it task.
        pool.submit(new Three()).get();
        pool.shutdown();
    }
}

Вывод этой программы::

One...
One!!
Two...
Two!!
Three...
Three!!

Вы можете увидеть, что занимает 6 секунд, прежде чем завершить свою задачу, которая больше, чем другой поток. Итак, Future.get() ждет, пока задача не будет выполнена.

Если вы не используете future.get(), он не ждет завершения и исполнения на основе времени.

Удача с Java concurrency.

Ответ 4

public class ThreadEvent {

    private final Object lock = new Object();

    public void signal() {
        synchronized (lock) {
            lock.notify();
        }
    }

    public void await() throws InterruptedException {
        synchronized (lock) {
            lock.wait();
        }
    }
}

Используйте этот класс следующим образом:

Создайте ThreadEvent:

ThreadEvent resultsReady = new ThreadEvent();

В методе это ждет результатов:

resultsReady.await();

И в методе, который создает результаты после создания всех результатов:

resultsReady.signal();

EDIT:

(Извините за редактирование этого сообщения, но у этого кода очень плохое состояние гонки, и у меня недостаточно репутации для комментариев)

Вы можете использовать это, только если вы на 100% уверены, что сигнал() вызывается после ожидания(). Это одна из главных причин, почему вы не можете использовать Java-объект, например, например. События Windows.

Если код работает в следующем порядке:

Thread 1: resultsReady.signal();
Thread 2: resultsReady.await();

тогда поток 2 будет ждать вечно. Это связано с тем, что Object.notify() только просыпает один из текущих выполняемых потоков. Поток, ожидающий позже, не проснулся. Это сильно отличается от того, как я ожидаю, что события будут работать, когда событие сигнализируется до тех пор, пока a) не будет ждать или b) явно reset.

Примечание. В большинстве случаев вы должны использовать notifyAll(), но это не относится к проблеме "wait forever" выше.

Ответ 5

Попробуйте CountDownLatch класс из пакета java.util.concurrent, который обеспечивает механизмы синхронизации более высокого уровня, которые гораздо менее подвержены ошибкам, чем любой материал низкого уровня.

Ответ 6

Вы можете сделать это, используя Exchanger объект, разделяемый между двумя потоками:

private Exchanger<String> myDataExchanger = new Exchanger<String>();

// Wait for thread output
String data;
try {
  data = myDataExchanger.exchange("");
} catch (InterruptedException e1) {
  // Handle Exceptions
}

И во втором потоке:

try {
    myDataExchanger.exchange(data)
} catch (InterruptedException e) {

}

Как говорили другие, не принимайте этот беззаботный и просто код для копирования-вставки. Сначала сделайте чтение.

Ответ 7

Множество правильных ответов, но без простого примера. Вот простой и легкий способ использования CountDownLatch:

//inside your currentThread.. lets call it Thread_Main
//1
final CountDownLatch latch = new CountDownLatch(1);

//2
// launch thread#2
new Thread(new Runnable() {
    @Override
    public void run() {
        //4
        //do your logic here in thread#2

        //then release the lock
        //5
        latch.countDown();
    }
}).start();

try {
    //3 this method will block the thread of latch untill its released later from thread#2
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

//6
// You reach here after  latch.countDown() is called from thread#2

Ответ 8

Future интерфейс из пакета java.lang.concurrent предназначен для обеспечения доступа к результатам, рассчитанным в другом потоке.

Взгляните на FutureTask и ExecutorService для готового способа делать такие вещи.

Я настоятельно рекомендую прочитать Java Concurrency In Practice всем, кто интересуется Concurrency и многопоточным. Очевидно, он концентрируется на Java, но есть много мяса для всех, кто работает на других языках.

Ответ 9

Это относится ко всем языкам:

Вы хотите иметь модель события/слушателя. Вы создаете слушателя для ожидания определенного события. Событие будет создано (или сигнализировано) в вашем рабочем потоке. Это заблокирует поток до тех пор, пока сигнал не будет получен, а не будет постоянно проверяться, будет ли выполнено условие, например, решение, которое у вас есть.

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

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

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

Thread-A Locks lock-a
Run thread-B
Thread-B waits for lock-a
Thread-A unlocks lock-a (causing Thread-B to continue)
Thread-A waits for lock-b 
Thread-B completes and unlocks lock-b

Ответ 10

Если вам нужно что-то быстрое и грязное, вы можете просто добавить вызов Thread.sleep() в цикл while. Если библиотека базы данных - это то, что вы не можете изменить, тогда нет другого простого решения. Опрос базы данных до тех пор, пока не будет готов с периодом ожидания, не убьет производительность.

while (!dbthread.isReady()) {
  Thread.sleep(250);
}

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

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

Ответ 11

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

Ответ 12

Так как

  • join() исключено
  • Вы уже используете CountDownLatch и
  • Future.get() уже предлагается другими экспертами,

Вы можете рассмотреть другие варианты:

  • invokeAll от ExecutorService

    invokeAll(Collection<? extends Callable<T>> tasks)
    

    Выполняет заданные задачи, возвращая список фьючерсов, сохраняющих их статус и результаты, когда все завершено.

  • ForkJoinPool или newWorkStealingPool from Executors (начиная с версии Java 8)

    Создает пул потоков обработки, используя все доступные процессоры в качестве целевого уровня parallelism.

Ответ 13

введите описание изображения здесь

Эта идея может применяться?. Если вы используете CountdownLatches или Semaphores, то отлично работает, но если вы ищете самый простой ответ на собеседование, я думаю, что это может быть применено.