Интервью: Как обеспечить, чтобы поток работал за другим?
Есть поток T1
, T2
и T3
, как мы можем гарантировать, что поток T2
запускается после T1
и потока T3
после T2
?
Этот вопрос задавали в моем интервью. Я не ответил. Пожалуйста, объясните подробно.
Ответы
Ответ 1
Это будет самый простой, самый тупой подход:
final Thread t1 = new Thread(new T1()); // assume T1 is a Runnable
t1.start();
t1.join();
final Thread t2 = new Thread(new T2());
t2.start();
t2.join();
final Thread t3 = new Thread(new T3());
t3.start();
t3.join();
Ответ 2
Очевидный и самый простой способ уже был отправлен @Assylias - есть метод запуска T1 create/start T2 и T2 запустить метод create/start T3.
Это, ИМХО, граничит с бессмысленным, но это можно сделать.
Решения, использующие Join(), не отвечают на вопрос - они гарантируют, что прекращение потоков упорядочено, а не их запуск. Если собеседник не понимает этого, вам все равно нужно найти другую работу.
В интервью, мой ответ будет "Для * ради чего? Темы используются обычно, чтобы избежать того, что вы просите!".
Ответ 3
В начале каждого потока (кроме t1) заставьте его вызвать join() на нем предшественника. Использование исполнителей (вместо прямых потоков) - это еще один вариант. Можно также взглянуть на использование семафоров - T1 должен освободить разрешение после завершения, T2 должен попытаться получить два разрешения и отпустить их, когда это будет сделано, T3 должен попытаться получить три разрешения и так далее. Использование соединения или исполнителей было бы предпочтительным маршрутом.
Ответ 4
Один из способов сделать это - это что-то вроде следующего. Это сложно. Для этого вы можете использовать класс java.util.concurrent.CyclicBarrier
.
Каждый поток, когда он заканчивается, устанавливает логическое значение и уведомляет следующий поток для продолжения. Даже если это класс AtomicBoolean
, нам нужен synchronized
, поэтому мы можем wait()
и notify()
на нем.
Было бы проще проходить объекты блокировки или, возможно, иметь метод begin()
на T2
и T3
, чтобы мы могли скрыть блокировки внутри этих объектов.
final Object lock2 = new Object();
final Object lock3 = new Object();
boolean ready2;
boolean ready3;
...
public T1 implements Runnable {
public void run() {
...
synchronized (lock2) {
// notify the T2 class that it should start
ready2 = true;
lock2.notify();
}
}
}
...
public T2 implements Runnable {
public void run() {
// the while loop takes care of errant signals
synchronized (lock2) {
while (!ready2) {
lock2.wait();
}
}
...
// notify the T3 class that it should start
synchronized (lock3) {
ready3 = true;
lock3.notify();
}
}
}
...
public T3 implements Runnable {
public void run() {
// the while loop takes care of errant signals
synchronized (lock3) {
while (!ready3) {
lock3.wait();
}
}
...
}
}
Ответ 5
Потоки также являются runnables. Вы можете просто запустить их последовательно:
t1.run();
t2.run();
t3.run();
Это явно не представляет интереса.
Предполагая, что они хотят, чтобы потоки выполнялись параллельно, одно решение заключалось бы в том, чтобы каждый поток запускал следующий, поскольку JMM гарантирует, что
Выполняется вызов start() в потоке - перед любыми действиями в запущенном потоке.
Ответ 6
Угадайте, что вопрос интервьюера состоял из трех потоков, которые работают в последовательности. Например, если один поток печатает 1,4,5... второй 2,5,8 и третья 3,6,9 и т.д., вывод должен быть 1,2,3,4,5.....
Ist thread prints 1 и дает возможность второму потоку печатать 2..etc.,
Я попробовал это с помощью циклических барьеров. Как только "один" печатает 1, дает шанс двум, так как он вызывает cb.wait, когда два прогона он в свою очередь вызовет три аналогичным образом, и он продолжит. Позвольте мне знать, если тр любые ошибки в коде
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
class one implements Runnable{
CyclicBarrier cb;
one(CyclicBarrier cb){this.cb=cb;}
public void run(){
int i=1;
while(true)
{
System.out.println(i);
try {
Thread.sleep(1000);
cb.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
i=i+3;
}
}
}
class two implements Runnable{
CyclicBarrier cb;
int i=2;
two(CyclicBarrier cb){this.cb=cb;}
public void run(){
System.out.println(i);
try {
cb.await();
i=i+3;
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class oneTwoThree {
public static void main(String args[]){
Runnable threePrinter = new Runnable() {
int i=3;
public void run() {
System.out.println(i);
i=i+3;
}
};
CyclicBarrier bar2 =new CyclicBarrier(1,threePrinter);//, barrier1Action);
two twoPrinter =new two(bar2);
CyclicBarrier bar1 =new CyclicBarrier(1,twoPrinter);
Thread onePrinter=new Thread(new one(bar1));
onePrinter.start();
}
}
Ответ 7
Я попробовал гораздо проще... используя ожидания и уведомления (в отличие от подхода с циклическим барьером в моем предыдущем сообщении).
Он использует класс 'State', который получает три состояния: 1,2,3. (по умолчанию 3).
Когда он равен 3, он запускает t1, при 1 срабатывает t2, а в 2 - t3 и т.д.
Классы:
Состояние //int я = 3
T1//печатает 1,4,7...
T2//Печать 2,5,8
T3//Печать 3,6,9 и т.д.,
Пожалуйста, дайте мне знать ваши мнения или любые проблемы в коде. Спасибо.
Вот код.:
public class State {
private int state ;
public State() {
this.state =3;
}
public synchronized int getState() {
return state;
}
public synchronized void setState(int state) {
this.state = state;
}
}
public class T1 implements Runnable {
State s;
public T1(State s) {
this.s =s;
}
@Override
public void run() {
int i =1;
while(i<50)
{
//System.out.println("s in t1 "+ s.getState());
while(s.getState() != 3)
{
synchronized(s)
{
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized(s)
{
//if(s.getState() ==3)
if(s.getState()==3)
System.out.println("t1 "+i);
s.setState(1);
i = i +3 ;
s.notifyAll();
}
}
}
}
public class T2 implements Runnable {
State s;
public T2(State s) {
this.s =s;
}
@Override
public synchronized void run() {
int i =2;
while(i<50)
{
while(s.getState() != 1)
{
synchronized(s)
{
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized(s)
{
//if(s.getState() ==3)
if(s.getState()==1)
System.out.println("t2 "+i);
s.setState(2);
i = i +3 ;
s.notifyAll();
}
}
}
}
public class T3 implements Runnable {
State s;
public T3(State s) {
this.s =s;
}
@Override
public synchronized void run() {
int i =3;
while(i<50)
{
while(s.getState() != 2)
{
synchronized(s)
{
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized(s)
{
if(s.getState()==2)
System.out.println("t3 "+i);
i = i +3 ;
s.setState(3);
s.notifyAll();
}
}
}}
public class T1t2t3 {
public static void main(String[] args) {
State s = new State();
Thread t1 = new Thread(new T1(s));
Thread t2 = new Thread(new T2(s));
Thread t3 = new Thread(new T3(s));
t1.start();
t2.start();
t3.start();
}
}
Ответ 8
Создайте очередь приоритетов с каждым протектором в другом, который они создали.
Затем вы можете применить Thread.join
после завершения, удалить этот поток из очереди приоритетов, а затем снова выполнить первый элемент очереди.
Псевдокод:
pthread [3] my_threads
my_queue
for t in pthreads:
my_queue.queue(t)
while !my_queue.empty()
pop the head of the queue
wait until it complets
thread.join()
реализация остается как упражнение, поэтому в следующий раз вы получите это правильно!
Ответ 9
Используйте метод threadAlive перед запуском потока T2 и T3.
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
Thread t3 = new Thread(new T3());
t1.start();
if(t1.isAlive()){
t2.start();
}
if(t2.isAlive()){
t3.start();
}
Ответ 10
Попробуйте использовать приведенный ниже код, используя при этом n количество потоков таким образом.
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicExecutionOfThreads {
public static void main(String args[]) {
int totalNumOfThreads = 10;
PrintJob printJob = new PrintJob(totalNumOfThreads);
/*
MyRunnable runnable = new MyRunnable(printJob, 1);
Thread t1 = new Thread(runnable);
MyRunnable runnable2 = new MyRunnable(printJob, 2);
Thread t2 = new Thread(runnable2);
MyRunnable runnable3 = new MyRunnable(printJob, 3);
Thread t3 = new Thread(runnable3);
t1.start();
t2.start();
t3.start();
*/
//OR
ExecutorService executorService = Executors
.newFixedThreadPool(totalNumOfThreads);
Set<Runnable> runnables = new HashSet<Runnable>();
for (int i = 1; i <= totalNumOfThreads; i++) {
MyRunnable command = new MyRunnable(printJob, i);
runnables.add(command);
executorService.execute(command);
}
executorService.shutdown();
}
}
class MyRunnable implements Runnable {
PrintJob printJob;
int threadNum;
public MyRunnable(PrintJob job, int threadNum) {
this.printJob = job;
this.threadNum = threadNum;
}
@Override
public void run() {
while (true) {
synchronized (printJob) {
if (threadNum == printJob.counter) {
printJob.printStuff();
if (printJob.counter != printJob.totalNumOfThreads) {
printJob.counter++;
} else {
System.out.println();
// reset the counter
printJob.resetCounter();
}
printJob.notifyAll();
} else {
try {
printJob.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class PrintJob {
int counter = 1;
int totalNumOfThreads;
PrintJob(int totalNumOfThreads) {
this.totalNumOfThreads = totalNumOfThreads;
}
public void printStuff() {
System.out.println("Thread " + Thread.currentThread().getName()
+ " is printing");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void resetCounter() {
this.counter = 1;
}
}
Ответ 11
package thread;
class SyncPrinter {
public static void main(String[] args) {
SyncPrinterAction printAction1 = new SyncPrinterAction(new int[]{1,5,9,13}, true);
SyncPrinterAction printAction2 = new SyncPrinterAction(new int[]{2,6,10,14}, true);
SyncPrinterAction printAction3 = new SyncPrinterAction(new int[]{3,7,11,15}, true);
SyncPrinterAction printAction4 = new SyncPrinterAction(new int[]{4,8,12,16}, false);
printAction1.setDependentAction(printAction4);
printAction2.setDependentAction(printAction1);
printAction3.setDependentAction(printAction2);
printAction4.setDependentAction(printAction3);
new Thread(printAction1, "T1").start();;
new Thread(printAction2, "T2").start();
new Thread(printAction3, "T3").start();
new Thread(printAction4, "T4").start();
}
}
class SyncPrinterAction implements Runnable {
private volatile boolean dependent;
private SyncPrinterAction dependentAction;
int[] data;
public void setDependentAction(SyncPrinterAction dependentAction){
this.dependentAction = dependentAction;
}
public SyncPrinterAction( int[] data, boolean dependent) {
this.data = data;
this.dependent = dependent;
}
public SyncPrinterAction( int[] data, SyncPrinterAction dependentAction, boolean dependent) {
this.dependentAction = dependentAction;
this.data = data;
this.dependent = dependent;
}
@Override
public void run() {
synchronized (this) {
for (int value : data) {
try {
while(dependentAction.isDependent())
//System.out.println("\t\t"+Thread.currentThread().getName() + " :: Waithing for dependent action to complete");
wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
dependentAction.setDependent(true);
System.out.println(Thread.currentThread().getName() + " :: " +value);
dependent = false;
}
}
}
private void setDependent(boolean dependent) {
this.dependent = dependent;
}
private boolean isDependent() {
return dependent;
}
}
Ответ 12
Параллельный пакет имеет лучшие классы для использования общего объекта.
Один из способов такой.
public static void main(String[] args) {
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
ThreadId threadId = new RunInSequence.ThreadId();
threadId.setId(1);
Thread t1 = setThread("thread1",lock, condition, 1, 2, threadId);
Thread t2 = setThread("thread2",lock, condition, 2, 3, threadId);
Thread t3 = setThread("thread3",lock, condition, 3, 1, threadId);
t1.start();
t2.start();
t3.start();
}
private static class ThreadId {
private int id;
public ThreadId() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
private static Thread setThread(final String name,final Lock lock, final Condition condition, int actualThreadId, int nextThreadId,
ThreadId threadId) {
Thread thread = new Thread() {
@Override
public void run() {
while (true) {
lock.lock();
try {
while (threadId.getId() != actualThreadId) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"prints: " + actualThreadId);
threadId.setId(nextThreadId);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
};
return thread;
}
Ответ 13
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class Worker implements Runnable {
BlockingQueue<Integer> q = new LinkedBlockingQueue<>();
Worker next = null; // next worker in the chain
public void setNext(Worker t) {
this.next = t;
}
public void accept(int i) {
q.add(i);
}
@Override
public void run() {
while (true) {
int i;
try {
i = q.take(); // this blocks the queue to fill-up
System.out.println(Thread.currentThread().getName() + i);
if (next != null) {
next.accept(i + 1); // Pass the next number to the next worker
}
Thread.sleep(500); // Just sleep to notice the printing.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class PrintNumbersSequentially {
public static void main(String[] as) {
Worker w1 = new Worker();
Worker w2 = new Worker();
Worker w3 = new Worker();
w1.setNext(w2);
w2.setNext(w3);
w3.setNext(w1);
new Thread(w1, "Thread-1: ").start();
new Thread(w2, "Thread-2: ").start();
new Thread(w3, "Thread-3: ").start();
//Till here all the threads have started, but no action takes place as the queue is not filled for any worker. So Just filling up one worker.
w1.accept(100);
}
}
Я думаю, это может помочь вам.
Ответ 14
Используя соединение, вы можете обеспечить выполнение потока один за другим.
class MyTestThread implements Runnable{
public void run() {
System.out.println("==MyTestThread : START : "+Thread.currentThread().getName());
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + " :i = "+i);
}
System.out.println("==MyTestThread : END : "+Thread.currentThread().getName());
}
}
public class ThreadJoinTest {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new MyTestThread(), "t1");
Thread thread2 = new Thread(new MyTestThread(), "t2");
thread1.start();
thread1.join();
thread2.start();
thread2.join();
System.out.println("====All threads execution===completed");
}
}
Ответ 15
как мы можем гарантировать, что поток T2 запускается после того, как T1 и поток T3 запускаются после T2?
NOTE: Assuming that it is not about scheduling the threads in the required order
Мы могли бы использовать Condition интерфейс.
Нам потребуются два условия, связанные с одним Lock: условие1 для координации T1 и T2, условие2 для координации T2 и T3.
Передать условие1 в T1 и T2, условие2 в T2 и T3.
Таким образом, мы ожидаем, что T2 будет ждать condition1 в этом методе run, который будет сигнализироваться T1 (из метода запуска T1, после того, как T1 начнет/завершит свою задачу). T3 ждет в condition2 в этом методе запуска, который будет сигнализироваться T2 (из метода запуска T2 после запуска/завершения задачи).
Ответ 16
Существуют потоки T1, T2 и T3, как мы можем гарантировать, что поток T2 запускается
после того, как T1 и поток T3 запускаются после T2?
ИЛИ
Есть три потока T1, T2 и T3? Как вы обеспечиваете последовательность T1, T2, T3 в Java?
Вопрос в основном заключается в том, что T3 должен закончить сначала, второй T2 и T1.
Мы можем использовать метод join() класса thread.
Чтобы обеспечить выполнение трех потоков, вам нужно сначала запустить последний, например, T3, а затем вызвать методы соединения в обратном порядке, например. T3 вызывает T2.join,
и T2 вызывает T1.join. Таким образом, T1 закончит сначала, а T3 завершит последний.
public class Test1 {
public static void main(String[] args) {
final Thread t1 = new Thread(new Runnable() {
public void run() {
System.out.println("start 1");
System.out.println("end 1");
}//run
});
final Thread t2 = new Thread(new Runnable() {
public void run() {
System.out.println(" start 2 ");
try {
t1.join(2000);
} catch (Exception e) {
e.getStackTrace();
}
System.out.println(" end 2");
}
}) ;
final Thread t3 = new Thread( new Runnable() {
public void run() {
System.out.println(" start 3 ");
try {
t2.join(4000);
}catch(Exception e) {
e.getStackTrace();
}
System.out.println(" end 3 ");
}
});
// we are reversing the order of the start() method
t3.start();
t2.start();
t1.start();
}
}
Из вывода вы увидите, что потоки запущены в другом порядке, так как вы не знаете, какой поток получит процессор. Это решение Планировщика потоков, поэтому мы ничего не можем сделать. Но вы можете видеть, что потоки закончены в правильном порядке, т.е. T1, затем T2, а затем T3.
Есть и другой способ сделать это. Псевдокод:
t1.start();
t1.join(); // signals t2 to wait
if( !t1.isAlive()) {
t2.start();// if t1 is finished then t2 will start
}
t2.join();//signals t3 to wait
if (!t2.isAlive()) {
t3.start();
}
Возьмем полную программу:
public class Tic implements Runnable{
public void run() {
try {
for (int i = 0; i < 2; i++) {
System.out.println("tic");
}
} catch (Exception e) {
// TODO: handle exception
e.getStackTrace();
}
}
}
public class Tac implements Runnable{
public void run() {
try {
for (int i = 0; i < 2; i++) {
System.out.println("tac");
}
} catch (Exception e) {
// TODO: handle exception
e.getStackTrace();
}
}
}
public class Toe implements Runnable{
public void run() {
try {
for (int i = 0; i < 2; i++) {
System.out.println("toe");
}
} catch (Exception e) {
// TODO: handle exception
e.getStackTrace();
}
}
}
public class RunThreads1 {
public static void main(String[] args) {
try {
Tic tic = new Tic();
Tac tac = new Tac();
Toe toe = new Toe();
Thread t1 = new Thread(tic);
Thread t2 = new Thread(tac);
Thread t3 = new Thread(toe);
t1.start();
t1.join(); // signals t2 to wait
if( !t1.isAlive()) {
t2.start();// if t1 is finished then t2 will start
}
t2.join();//signals t3 to wait
if (!t2.isAlive()) {
t3.start();
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
Выход:
тик
тик
нолики
нолики
палец
схождение