控制多个线程的执行流程

时间:2015-03-15 06:51:52

标签: java multithreading

我有4个线程在运行,即A,B,C,D。 根据某些输入条件,执行顺序是变化的。 例如,对于输入" 0",序列将是A-> C-> B-D 对于输入" 1",序列将是A-> D-> B-C

4 个答案:

答案 0 :(得分:1)

通常,您不应该想要以如此精细的细节控制线程。但是,有时需要。以编写测试用例为例。所以我为此准备了一个线程调度程序:

github.com/martinanderssondotcom/java-ee-concepts/blob/master/src/test/java/com/martinandersson/javaee/utils/ThreadScheduler.java

滚动到文件的底部,您可以看到有一个main method来说明此类的用法。在为Java EE编写测试代码时帮助了我很多。

答案 1 :(得分:0)

您可以等待第一个线程完成然后开始下一个线程:

Thread A = ...;
Thread B = ...;

A.start(); // start thread A
A.join(); // wait here until thread A finishes
B.start();
B.join();
// and so on.

答案 2 :(得分:0)

解决此问题的可能方法如下:

  1. 创建Thread类的ArrayList
  2. 按照您要执行它们的顺序将Thread实例添加到此ArrayList。
  3. 迭代ArrayList并逐个调用每个线程上的start方法和join方法。
  4. 应该运行线程的顺序将由您将它们插入ArrayList的顺序控制。

    完整的工作示例如下:

    public class ThreadSequencer {
    
        private static List<Thread> threadSequence = new ArrayList<>();
        /*Create the threads. If not using Java 8, replace ThreadSequencer::printThreadName
         * with new Runnable() { public void run() { System.out.println(Thread.currentThread().getName());} }
         */
        private static Thread threadA = new Thread(ThreadSequencer::printThreadName);
        private static Thread threadB = new Thread(ThreadSequencer::printThreadName);
        private static Thread threadC = new Thread(ThreadSequencer::printThreadName);
        private static Thread threadD = new Thread(ThreadSequencer::printThreadName);
    
    
        private static void sequenceThreads(String input) {
            threadSequence.clear();
            if("0".equals(input)) {
                threadSequence.add(threadA);
                threadSequence.add(threadB);
                threadSequence.add(threadC);
                threadSequence.add(threadD);
            } else if("1".equals(input)) {
                threadSequence.add(threadB);
                threadSequence.add(threadA);
                threadSequence.add(threadC);
                threadSequence.add(threadD);
            }
        }
    
        public static void runThreads(String input) {
            sequenceThreads(input);
            for(Thread thread : threadSequence) {
                thread.start();
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    //handle exceptional cases
                    e.printStackTrace();
                }
            }
        }
    
        private static void printThreadName() {
            System.out.println(Thread.currentThread().getName());
        }
    
        public static void main(String []args) {
            runThreads("1");
        }
    }
    

    使用String调用runThreads方法,线程将按该序列运行。话虽这么说,如果你不想按顺序运行线程,为什么要首先创建线程?

答案 3 :(得分:-1)

这个答案基于OPs表示法的假设读数, “X-> Y-Z”表示“线程X在Y和Z开始之前运行完成”。

修补工程。 Fouad回答OP的情况:

Thread A = ...;
Thread B = ...;
Thread C ...
Thread D ...

if (input==0)
  {  A.start(); // start thread A
     A.join(); // wait here until thread A finishes
     C.start();
     C.join();
     B.start();
     D.start();
     B.join();
     D.join();
   }
else
  {  A.start(); 
     A.join(); 
     D.start();
     D.join();
     B.start();
     C.start();
     B.join();
     C.join();
   }

作为一种轻微的优化,A.start(); A.join()语句对可以提升到条件之上。

OP似乎想要做的是在线程上定义动态部分订单。他的具体例子如上所述是可以解决的。通常,父线程使用“开始”和“连接”不能完成静态部分顺序(上面“if”的每个臂);各个线程必须进行连接以确保以正确的顺序调用它们。要执行动态部分订单,每个线程都必须根据条件检查前置任务。

假设输入== 0导致A-> B,A-> C,D-> C,否则A-> D,B-> D,B-> C: 你需要一个更加混乱的解决方案(原谅我的Java,我不是专家):

Thread A = new Thread(workerA)...;
Thread B = new Thread(workerB)...;
Thread C = new Thread(workerC)...;
Thread D = new Thread(workerD)...;

workerA() {
   ... 
}

workerB() {
   if (input==0)
       A.join();
   ...
}

workerC() {
  if (input==0)
     { A.join(); D.join(); }
  else B.join();
...
}

workerD() {
  if (input==1)
     { A.join(); B.join(); }
  ...
}

A.start(); // start thread A
B.start();
C.start();
D.start();
if (input==0)
   { B.join(); C.join(); }
else { C.join(); D.join(); }

我假设Java线程可以看到父线程中的变量(如果布局合理)。我还假设您可以多次加入一个主题,例如,从各个地方多次调用A.join()都可以。

如果您有10-12个具有复杂偏序的线程,则很难编写并正确使用它。有些语言可以直接表达部分订单,以避免编写所有启动/加入goo。