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

Меня попросили в интервью.

Есть четыре потока t1, t2, t3 и t4. t1 выполняет синхронизированный блок, а остальные потоки ждут завершения t1. Какую операцию вы бы сделали, чтобы t3 выполнялся после t1.

Я ответил, что метод join должен делать трюк, но похоже, что это не правильный ответ. Причина, по которой он дал, - это метод соединения, а метод setPriority не будет работать с потоками, которые находятся в состоянии ожидания.

Можем ли мы достичь этого? Если да, то как?

Ответ 1

Думаю, я бы использовал некоторые защелки. Один обратный отсчет между t1 и t2, другой - между t2 и t3, последний между t3 и t4. T1 Заканчивается с countDown, а t2 запускает синхронизированную часть с ожиданием.

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

Я не могу сказать, что это элегантно, хотя.

Ответ 2

Вы можете использовать блокировки и условия. Переходите к одному и тому же условию как t1, так и t3:

class Junk {

   private static class SequencedRunnable implements Runnable {
       private final String name;
       private final Lock sync;
       private final Condition toWaitFor;
       private final Condition toSignalOn;

       public SequencedRunnable(String name, Lock sync, Condition toWaitFor, Condition toSignalOn) {
           this.toWaitFor = toWaitFor;
           this.toSignalOn = toSignalOn;
           this.name = name;
           this.sync = sync;
       }

       public void run() {
           sync.lock();
           try {
               if (toWaitFor != null)
                   try {
                       System.out.println(name +": waiting for event");
                       toWaitFor.await();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               System.out.println(name + ": doing useful stuff...");
               if (toSignalOn != null)
                   toSignalOn.signalAll();
           } finally {
               sync.unlock();
           }
       }
   }

   public static void main(String[] args) {
       Lock l = new ReentrantLock();
       Condition start = l.newCondition();
       Condition t3AfterT1 = l.newCondition();
       Condition allOthers = l.newCondition();
       Thread t1 = new Thread(new SequencedRunnable("t1", l, start, t3AfterT1));
       Thread t2 = new Thread(new SequencedRunnable("t2", l, allOthers, allOthers));
       Thread t3 = new Thread(new SequencedRunnable("t3", l, t3AfterT1, allOthers));
       Thread t4 = new Thread(new SequencedRunnable("t4", l, allOthers, allOthers));

       t1.start();
       t2.start();
       t3.start();
       t4.start();

       l.lock();
       try {
           start.signalAll();
       } finally {
           l.unlock();
       }
   }
}

Ответ 3

Каждый поток должен просто ждать() на отдельном объекте. Поэтому t3 должен ждать t3Mutex. Затем вы можете просто сообщить об этом конкретном потоке.

final Object t1Mutex = new Object();
final Object t3Mutex = new Object();
...
synchronized(t3Mutex) {
    //let thread3 sleep
    while(condition) t3Mutex.wait();
}
...
synchronized(t1Mutex) {
   //do work, thread1
   synchronized(t3Mutex) {t3Mutex.notify();}
}

Ответ 4

Я не знаю какого-либо стандартного способа, но я предполагаю, что пропустил бы какой-то токен, и только тот, у которого есть токен, разрешено выполнять... другие yield(). Таким образом, t1 даст токен t3, когда он закончит. Но, возможно, есть лучший способ сделать это.

Также для этого потребуется использовать notifyAll() вместо notify().

Ответ 5

Вы хотели бы использовать метод notify() в блоке синхронизации t1.

http://www.janeg.ca/scjp/threads/notify.html

EDIT:

Я допустил ошибку в отношении notify() выше, это будет по усмотрению JVM, однако если t2 и t4 join() to to t3, то этот метод должен работать.

Ответ 6

если T1 был выполнен, мы могли бы использовать флаг, позволяющий запускать только T3. Как только выполнение T3 завершится, оставшиеся потоки могут выполнить.

Теперь это может быть не изящное решение.

Но с точки зрения интервью это продемонстрирует, что вы понимаете wait и notifyall.

Но опять же зависит от человека, который принимает интервью.

public class ThreadSequenceTest implements Runnable {
    Object o = new Object();
    volatile boolean t3Only = false;

    public void run() {
        synchronized (o) {
            if (Thread.currentThread().getName().equals("t1")) {
                doSomething();
                t3Only = true;
            } else {
                if (t3Only) {
                    if (Thread.currentThread().getName().equals("t3")) {
                        doSomething();
                        t3Only = false;
                        o.notifyAll();
                    } else {
                        try {
                            System.out.println("going to sleep " + Thread.currentThread().getName());
                            o.wait();
                            doSomething();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } else {
                    doSomething();
                }

            }
        }
    }

    private void doSomething() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadSequenceTest threadSequenceTest = new ThreadSequenceTest();
        Thread t1 = new Thread(threadSequenceTest);
        t1.setName("t1");
        Thread t2 = new Thread(threadSequenceTest);
        t2.setName("t2");
        Thread t3 = new Thread(threadSequenceTest);
        t3.setName("t3");
        Thread t4 = new Thread(threadSequenceTest);
        t4.setName("t4");

        t1.start();
        Thread.sleep(500);
        t2.start();
        t3.start();
        t4.start();

    }
}

Ответ 7

package manufacturer.consumer;

импортировать java.util.ArrayList; import java.util.List;

открытый класс ThreadInterComm {

public static void main(String args[]) {

    List<Integer> sharedObject = new ArrayList<Integer>(1);
    sharedObject.add(new Integer(0));

    Runnable task = new MyTask(sharedObject);

    Thread t1 = new Thread(task, "T1");
    Thread t2 = new Thread(task, "T2");
    Thread t3 = new Thread(task, "T3");

    t1.start();
    t2.start();
    t3.start();

}

}

класс MyTask реализует Runnable {

private final List<Integer> sharedObject;
String name = "T1";//Initializing with T1 

public MyTask(List<Integer> sharedObject) {
    this.sharedObject = sharedObject;
}

public void run() {

    synchronized (sharedObject) {
        while (true) {//Or use a counter how many times to do the job
            if (!name.equals(Thread.currentThread().getName())) {
                try {
                    sharedObject.wait();//Let other Threads wait
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (name.equals(Thread.currentThread().getName())) {
                int value = sharedObject.remove(0).intValue();
                sharedObject.add(new Integer(++value));
                System.out.println(Thread.currentThread().getName() + " : "
                        + sharedObject.get(0));
                if (Thread.currentThread().getName().equals("T1")) {

                    name = "T2";// give lock to t2
                } else if (Thread.currentThread().getName().equals("T2")) {

                    name = "T3";// give lock to t3
                } else if (Thread.currentThread().getName().equals("T3")) {

                    name = "T1";// give lock to t1
                }
                i--;
                sharedObject.notifyAll();
            }

    }}

}

}