LINUX.ORG.RU

Java многопоточность

 


0

2

Доброго времени суток, господа.

Есть следующая задача: есть два потока, которые должны работать n-oe количество времени (возможно, сутки и более) и должны запускаться и работать один за другим, причем сначала первый и только потом второй (гарантированно). Пробовал разными способами, но даже установка приоритета не гарантирует такого способа выполнения. Реально ли такого вообще достичь? Или пытаться делать каким-то другим способом?

Не понял, почему не сделать так, чтобы первый поток запускал второй/запрашивал запуск второго?

Что за задачу они пытаются выполнить?

Возьми какой-нибудь Executors.newFixedThreadPool(2) и кидай туда задачи когда надо. Если настало время запуска второго треда, то просто кинь задачу в executor.
Ну, или если хочется геморроя с wait/notifyAll, то делай через них, стартуй два потока, но второй пусть ждет, пока первый не доделает свою порцию работы и не вызовет notifyAll на локе, разрешив тем самым начать работу второму треду.

kovrik ★★★★★
()
Последнее исправление: kovrik (всего исправлений: 1)

CoutDownLatch. Сделай какой-нибудь класс, для синхронизации, в нем сделай final CDL поле, которое инициализируются в конструкторе со значением «1». Первый поток будет сделать cdl.coundDown(), когда закончил, второй поток будет делать cdl.await() перед тем как вступить в работу. Запускаешь оба потока и все хорошо.

DiKeert ★★
()

Вот код

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main {

    private static final CountDownLatch latch = new CountDownLatch(1);

    static class CompletionMonitor {
        private final CountDownLatch latch = new CountDownLatch(1);

        public void complete() {
            latch.countDown();
        }

        public void await() {
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        public boolean await(long timeout, TimeUnit unit) {
            try {
                return latch.await(timeout, unit);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    static class Worker implements Runnable {
        private final CompletionMonitor monitor;
        private final boolean wait;

        public Worker() {
            this.monitor = new CompletionMonitor();
            wait = false;
        }

        public Worker(CompletionMonitor monitor) {
            this.monitor = monitor;
            wait = true;
        }

        public CompletionMonitor getMonitor() {
            return monitor;
        }

        @Override
        public void run() {
            if(wait) {
                System.out.println(Thread.currentThread().getName() + ": Waiting for completion");
                monitor.await();
                System.out.println(Thread.currentThread().getName() + ": Wait completed, working now.");
            } else {
                System.out.println(Thread.currentThread().getName() + ": Starting right now");
                System.out.println(Thread.currentThread().getName() + ": Working!");
                try {
                    Thread.sleep(10000L);
                } catch (InterruptedException e) {
                    //we really can't be fucked processing that exception
                } finally {
                    System.out.println(Thread.currentThread().getName() + ": Completing!");
                    monitor.complete();
                }
            }
        }
    }


    public static void main(String[] args) {
        ExecutorService e = Executors.newFixedThreadPool(2);
        Worker worker1 = new Worker();
        Worker worker2 = new Worker(worker1.getMonitor());
        e.submit(worker1);
        e.submit(worker2);
    }
}

DiKeert ★★
()
Последнее исправление: DiKeert (всего исправлений: 2)
Ответ на: комментарий от DiKeert

Еще вариант - сделать очередь и накидать туда Runnable-ов, и обрабатывать эту очередь в одном потоке, потому что непонятно, зачем тебе в этом случае два поток, если они работают последовательно? Но даже если нужны разные потоки - сделать ExecutorService, скормить ему ПРАВИЛЬНУЮ, т.е. такую, которая выплевывает объекты в том же порядке, что их туда положили, очередь и напихать в нее Runnable-ов, а эту очередь скормить ExecutorService.

DiKeert ★★
()
Ответ на: комментарий от kovrik

Это что-то наподобие таймера. Один поток считает время, а второй должен запускать события по наступлению определенных моментов времени. И нужно сделать так, чтобы они каждый раз выполнялись строго последовательно, но, похоже, это нереально.

misterious
() автор топика
Ответ на: комментарий от DiKeert

Да я вот таким способом реализовывал, но, периодически на консоль выводом на экран показывает неправильный порядок :(

misterious
() автор топика
Ответ на: комментарий от misterious

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

DiKeert ★★
()
Ответ на: комментарий от misterious

Вообще для такой задачи всякие скедулеры существуют. Quartz тот же.

DiKeert ★★
()
Ответ на: комментарий от misterious

Не совсем понял зачем отдельный поток считает время.
Т.е. по факту реальную работу выполняет только один поток?
Почему тогда не делать все в одном потоке?
Считаешь время в одном потоке, а когда надо запускать задачу - просто из этого же треда и запускаешь эту задачу (в отдельном треде/кидаешь ее в ExecutorService).

Ну или использовать ScheduledExecutorService (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecu...)

Или тот же Quartz уже советовали (http://www.quartz-scheduler.org/).

kovrik ★★★★★
()

Дискретно-событийное моделирование? Если да, то потоки там будут слишком наивной реализацией, если мягко выразиться

dave ★★★★★
()

Собеседование что ли проходишь? ))) Это ж стандартная ping-pong задача по многопоточности. Взгляни на статью №51 в книге Д. Блоха «Java. Эффективное программирования»

ii8_ ★★★★
()
public class TwoThreads {
  void thread0() {
    System.out.println("Thread 0");
  }
  void thread1() {
    System.out.println("Thread 1");
  }
  public static void main(String []args) {
    thread0();
    thread1();
  }
}

Так не вариант? ЗЫ. не тестировал.

deadskif
()
Ответ на: комментарий от kovrik

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

misterious
() автор топика
Ответ на: комментарий от misterious

Встанут в очередь:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html...

If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available.

Еще обрати внимание на то, как надо его убивать (shutdown) https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorServic...

Смотри там пример после слов 'The following method shuts down an ExecutorService in two phases, first by calling shutdown to reject incoming tasks, and then calling shutdownNow, if necessary, to cancel any lingering tasks:'

kovrik ★★★★★
()

Есть следующая задача: есть два потока, которые должны работать n-oe количество времени (возможно, сутки и более) и должны запускаться и работать один за другим, причем сначала первый и только потом второй (гарантированно)

И нужно сделать так, чтобы они каждый раз выполнялись строго последовательно

И на кой чёрт тебе потоки, если всё последовательно?

korvin_ ★★★★★
()

Метод join()

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

Метод join() имеет перегруженную версию, которая получает в качестве параметра время ожидания. В этом случае join() возвращает управление либо когда завершится ожидаемый поток, либо когда закончится время ожидания. Подобно методу Thread.sleep() метод join может ждать в течение миллисекунд и наносекунд – аргументы те же.

- https://habrahabr.ru/post/164487/

iZEN ★★★★★
()

Традиционный пинг-понг, но в этом случае многопоточность не нужна.

Если хочется, сделай на акторах. Для примера накидал сессию для ammonite, попробуй, если будет охота.

interp.load.ivy("com.typesafe.akka" % "akka-actor_2.11" % "2.4.10")

import akka._
import akka.actor._
import scala.compat.Platform
import scala.util.Random

case class Start(ref: ActorRef)
case object Run

class Timer extends Actor {
  def receive = {
    case Start(worker: ActorRef) =>
      val time = Random.nextInt(1000);
      println(s"waiting for $time ms"); 
      Thread.sleep(time); // так не делают, но для эмуляции задержек прокатит
      worker ! Run
  }
}

class Worker extends Actor {
  def receive = {
    case Run =>
      println(s"run @ ${Platform.currentTime}");
      sender() ! Start(self)
  }
}

val system = ActorSystem()
val w = system.actorOf(Props(classOf[Worker]))
val t = system.actorOf(Props(classOf[Timer]))
t ! Start(w)

cdshines ★★★★★
()
Последнее исправление: cdshines (всего исправлений: 1)
Ответ на: комментарий от korvin_

А как с помощью join организовать поочерёдное выполнение двух потоков?

Примитивным образом: из последнего потока делаешь join() к предпоследнему, повторяешь для всех в очереди.

Но можно не заморачиваться и делать старт следующего в очереди потока в теле выполнения предыдущего, точнее, в секции finally { second.start(); }, чтобы гарантировать запуск при сбое.

iZEN ★★★★★
()

делай join к первому потоку, и запускай второй..

но вообще непонятно, зачем два потока, если надо последовательно.

upd: долбаные некроманты

waker ★★★★★
()
Последнее исправление: waker (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.