如何确保线程的执行顺序

huangapple go评论55阅读模式
英文:

How to ensure execution order of threads

问题

这是问题的简化版本。给定 n 个线程,每个线程都不断打印一个常数。例如,Thread-1 应该始终打印 1Thread-2 应该始终打印 2,以此类推...

如何确保线程按顺序执行,即输出应如下所示:

Thread-1: 1
Thread-2: 2
Thread-3: 3
.
.
.
Thread-n: n

我有一个比较简单的解决方案是使用 wait()/notify(),但我想可能有比那更好的解决方案。也许可以使用 Semaphore?我不太清楚。

更新

根据收到的答案,我认为我表达得不够清楚。有一些约束条件:

  1. 所有线程应该同时开始(假设我们对此没有实际控制)
  2. 一旦所有线程开始,线程之间应该有某种通信以按顺序执行。
英文:

This is a simplified version of the problem. Given n number of threads, each printing a constant number all the time. For example, Thread-1 should always print 1, Thread-2 should always print 2 and so on...

How to ensure, the threads are executed in order i.e. the output should be as below:

Thread-1: 1
Thread-2: 2
Thread-3: 3
.
.
.
Thread-n: n

I have a naïve solution to do it through wait()/notify() but I guess there might be a better solution than that. Perhaps, using Semaphore maybe? I don't know.

Update:

Based on the answers received, I think I was not very clear. There are some constraints:

  1. All threads should start at once (assume we don't really have control on that)
  2. Once all the threads start, there should be some sort of communication between the threads to execute in order.

答案1

得分: 1

这种线程的顺序执行可以使用 Thread.join() 方法来很好地处理。为了正确处理它,您可能需要创建一个实现 Runnable 接口的 MyRunnable(或者使用您喜欢的任何名称)。在 MyRunnable 内部,您可以注入一个 parent 线程,并在 MyRunnable.run() 方法的顶部调用 parent.join()。以下是代码:

public class SequentialThreadsTest {

	static class MyRunnable implements Runnable {
		static int objCount; // 用于保持顺序对象的计数

		private int objNum;
		private Thread parent; // 跟踪父线程
		
		MyRunnable(Thread parent) {
			this.parent = parent;
			this.objNum = objCount + 1;
			objCount += 1;
		}

		@Override
		public void run() {
			try {
				if (parent != null) {
					parent.join();
				}
				System.out.println("Thread-" + objNum + ": " + objNum);

			} catch (InterruptedException e) {
				e.printStackTrace();
				// 做其他事情

			} finally {
				// 在线程执行完成时执行所需的操作
			}
		}
	}

	public static void main(String[] args) {
		int n = 10;
		
		Thread parentThread = null;

		for (int i = 0; i < n; i++) {
			Thread thread = new Thread(new MyRunnable(parentThread));
			thread.start();

			parentThread = thread;
		}
	}
}

输出结果为:

Thread-1: 1
Thread-2: 2
Thread-3: 3
Thread-4: 4
Thread-5: 5
Thread-6: 6
Thread-7: 7
Thread-8: 8
Thread-9: 9
Thread-10: 10
英文:

This sequentially execution of thread can be handled beautifully using Thread.join() method. To handle it properly, you may have to create MyRunnable(or, use any name you prefer) which implements Runnable interface. Inside MyRunnable, you can inject a parent Thread, and call parent.join() at top of MyRunnable.run() method. The code is given below:

public class SequentialThreadsTest {

	static class MyRunnable implements Runnable {
		static int objCount; // to keep count of sequential object

		private int objNum;
		private Thread parent; // keep track of parent thread
		
		MyRunnable(Thread parent) {
			this.parent = parent;
			this.objNum = objCount + 1;
			objCount += 1;
		}

		@Override
		public void run() {
			try {
				if(parent != null) {
					parent.join();
				}
				System.out.println(&quot;Thread-&quot; + objNum + &quot;: &quot; + objNum);

			} catch(InterruptedException e) {
				e.printStackTrace();
				// do something else

			} finally {
				// do what you need to do when thread execution is finished
			}
		}
	}

	public static void main(String[] args) {
		int n = 10;
		
		Thread parentThread = null;

		for(int i=0; i&lt;n; i++) {
			Thread thread = new Thread(new MyRunnable(parentThread));
			thread.start();

			parentThread = thread;
		}
	}
}

And the output is:

Thread-1: 1
Thread-2: 2
Thread-3: 3
Thread-4: 4
Thread-5: 5
Thread-6: 6
Thread-7: 7
Thread-8: 8
Thread-9: 9
Thread-10: 10

答案2

得分: 0

你没有指定很多细节,但如果你只想要可串行化的线程执行,你可以等待前一个线程完成后再进行打印。类似这样:

public static void main(String[] args) {
    Thread thread = null;
    for (int i = 0; i < 10; i++) {
        int index = i;
        Thread previousThread = thread;
        thread = new Thread(() -> {
            if (previousThread != null) {
                try {
                    previousThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(index);
        });
        thread.start();
    }
}
英文:

You haven't specified many details, but if you only want serializable thread execution you can wait for previous thread to finish and then print. Something like this:

public static void main(String[] args) {
        Thread thread = null;
        for (int i = 0; i &lt; 10; i++) {
            int index = i;
            Thread previousThread = thread;
            thread = new Thread(() -&gt; {
                if (previousThread != null) {
                    try {
                        previousThread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(index);
            });
            thread.start();
        }
    }

答案3

得分: 0

尝试创建一个队列 - 这将完全按照您的要求执行。只需更改 n 的值为您拥有的线程数量,并按顺序添加所有线程(仅添加一次)。如果您想要停止执行线程,只需将 end 添加到队列中。显然,对于更大的项目,您需要稍微修改此代码(我建议用类初始化程序替换 main 方法,并将 LinkedBlockingQueue 作为预先构建的参数传递)

import java.util.concurrent.LinkedBlockingQueue;

public class HelloWorld{

    private static int n = 2;
    private static LinkedBlockingQueue<Thread> queue = new LinkedBlockingQueue<>(n+1);

    static Thread a = new Thread(()->{
            System.out.print("a");

        });

    static Thread b = new Thread(()->{
            System.out.print("b");

        });    

    static Thread end = new Thread(()->{
            break_ = true;

        }); 

    public static final int END = 20; // 这个和计数器只是为了防止代码无限运行
    public static volatile int i = 0;
    public static volatile boolean break_ = false;
    public static void main(String []args){

        queue.add(a);
        queue.add(b);
        // queue.add(end);

        outerloop:
        while(true){

            Thread toBeRun = queue.poll();
            try{
                toBeRun.run();
                queue.add(toBeRun);
                i++;
                if(i>=END || break_){ // i>=END 可以不需要,只是为了防止在此示例中无限运行
                    break;
                }
            }catch(NullPointerException e){
                break;
            }
        }
     }
}

注意:这使用了 Java 8 的 lambda 表达式。如果您使用较旧版本的 Java,则需要使用 run 方法创建线程。

英文:

Try making a queue - this will do exactly what you want. Simply change the value of n to however many threads you have, and add all the threads sequentially (only once). If ever you want to stop the threads from executing, all you have to do is add end to the queue. Obviously, for a larger project, you will need to modify this code a little bit (I would recommend replacing the main method with a class initializer and pass the LinkedBlockingQueue as a pre-built argument)

import java.util.concurrent.LinkedBlockingQueue;



public class HelloWorld{
    
    private static int n = 2;
    private static LinkedBlockingQueue&lt;Thread&gt; queue = new LinkedBlockingQueue&lt;&gt;(n+1);
    
    static Thread a = new Thread(()-&gt;{
            System.out.print(&quot;a&quot;);
        
        });
        
    static Thread b = new Thread(()-&gt;{
            System.out.print(&quot;b&quot;);
        
        });    
        
    static Thread end = new Thread(()-&gt;{
            break_ = true;
        
        }); 


    
    public static final int END = 20;//this and the counter are just here so the code doesn&#39;t run forever
    public static volatile int i = 0;
    public static volatile boolean break_ = false;
    public static void main(String []args){
        
        queue.add(a);
        queue.add(b);
        //queue.add(end);
        
        
        outerloop:
        while(true){
            
            Thread toBeRun = queue.poll();
            try{
                toBeRun.run();
                queue.add(toBeRun);
                i++;
                if(i&gt;=END || break_){//i&gt;=END does not need to be here, it&#39;s just to stop it from running forever in this example
                    break;
                }
            }catch(NullPointerException e){
                break;
            }
            
            
            
            
        }
        
     }
}

Note: This uses java 8 lambdas. If you're using an older version of java, you will need to create the threads using the run method.

huangapple
  • 本文由 发表于 2020年8月5日 02:23:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/63252957.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定