【问题标题】:Displaying threads alternatively in JAVA在 JAVA 中交替显示线程
【发布时间】:2022-01-15 17:09:34
【问题描述】:

我想像这样交替显示这两个线程:

  • 线程 1
  • 线程 0
  • 线程 1
  • 线程 0 ...

这是我开始的基本代码,我尝试了 wait() notify() 方法,但我无法得到想要的结果。

class Task extends Thread {
    @Override
    public void run() {
        try {
            for(int i = 0; i<10; i++){

                double dure = Math.random()*200 ;
                sleep((long) dure);
                System.out.println(Thread.currentThread().getName());
            }
        } catch (Exception e) {
        }
    }
}

public class App {
    public static void main(String[] args) {

        Task t1 = new Task() ;
        Task t2 = new Task() ;

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


        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
        }
        
        
    }
    
} ```


 

【问题讨论】:

  • 你到底想在这里实现什么?如果您只是想打印他们的名字,请在计算睡眠持续时间时取出 Math.random() 调用。对于其他一切,您可能需要添加有关您实际想要实现的目标的详细信息以获得帮助
  • @meaningqo 我不想要那种结果 thread1、thread1、thread0。我希望每次 thread0 后跟 thread1 或相反。我把 math.random 给每个线程一个小于 200 毫秒的任意时间来休眠。
  • 使用的随机时间“sleep((long) dure);”使得输出“随机”
  • 很难真正回答这个问题,因为显然有一个未说明的要求:如果您只想打印交替消息,最简单的方法是只使用一个线程。那么——你想用这两个线程练习什么? CyclicBarrier 或 Exchanger(都在 java.util.concurrent 中)可以做类似的事情,但它们比等待/通知更高级别。另一方面,您似乎可以不使用等待/通知。因此,您需要在这里告诉我们您真正想要完成或学习的内容,并且比“使用线程”更具体一级。

标签: java multithreading java-threads


【解决方案1】:

我看到了两种解决方案:

  1. 忙等

每个线程在打印前等待。并在条件为真时释放。我将AtomicInteger 用于indexToPrint 以使每个线程的此值同步。

此解决方案适用于 n 个线程。

import java.util.concurrent.atomic.AtomicInteger;

class Task extends Thread {
    
    final static private AtomicInteger indexToPrint = new AtomicInteger(0);

    static private int threadNumber = 0;

    final private int index;

    /**
     *
     */
    public Task() {
        index = threadNumber++;
    }


    private int nextIndex() {
        return (index + 1) % threadNumber;
    }

    @Override
    public void run() {

        try {    

            for(int i = 0; i<10; i++){

                double dure = Math.random()*200 ;
                sleep((long) dure);

                while (indexToPrint.get() != index) {
                    sleep((long) 10);
                }
                indexToPrint.set(nextIndex());
                
                System.out.println(Thread.currentThread().getName());
                
            }
        } catch (Exception e) {}
    }
}
  1. 等待并通知

理解起来有点复杂,但没有无用的 CPU 使用。让我们解释一下同步块synchronized (indexToPrint) {...} 是如何工作的。 该块同步监视静态对象indexToPrint。这个对象是静态的(每个线程共有),所以只有一个线程可以同时进入这个块。

当一个线程进入块时,如果它的indexindexToPrint 不同,则该线程以wait() 停止,从而使另一个线程可以进入块。否则,线程名称被打印,indexToPrint 更新为下一个线程索引,所有线程都被notifyAll() 唤醒。最后,它离开了街区。

所有等待的线程现在都醒了,实际的线程离开了块。所以一个线程可以再次尝试打印。

重要的是要理解,当一个线程等待然后通知时,它会在它停止的地方运行。在这里,一个线程可以在两个位置停止:同步块之前和等待调用。

while 在这里非常重要。所有线程都以notifyAll() 唤醒,所以在唤醒后他们应该再次测试自己。

你可以找到一个好的documentation here

代码基于上一个。与indexToPrint 相同。

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

class Task extends Thread {

    static private final AtomicInteger indexToPrint = new AtomicInteger(0);

    static private int threadNumber = 0;

    final private int index;

    final private static ArrayList<Task> tasks = new ArrayList<>();

    /**
     *
     */
    public Task() {
        index = threadNumber++;
        tasks.add(this);
    }


    private int nextIndex() {
        return (index + 1) % threadNumber;
    }

    @Override
    public void run() {

        try {

            for(int i = 0; i<10; i++){

                double dure = Math.random()*200 ;
                sleep((long) dure);

                synchronized (indexToPrint) {
                    while (indexToPrint.get() != index) {
                        indexToPrint.wait();
                    }
                    indexToPrint.set(nextIndex());
                    System.out.println(Thread.currentThread().getName());
                    indexToPrint.notifyAll();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

  • 忙等待的代码很丑,第二个很好。如果有一个关于如何在这里使用等待的解释以使其给出正确的结果,那将会得到改进。 (我认为,链接的文章并没有很好地解释它。)
  • @NathanHughes 你现在认为清楚了吗?我应该删除关于忙等待的第一部分吗?
  • 这是一个很大的改进。我已经对此表示赞同。
  • 太棒了!谢谢你。
【解决方案2】:

随机休眠时间也可能导致意外结果在main方法中使主线程在线程1和线程2开始之间休眠可以帮助您知道谁是第一个启动打印任务的线程,之后您应该在任务中给予正确的睡眠时间,让线程可以交替打印。

class Task extends Thread {
@Override
public void run() {
    try {
        for(int i = 0; i<10; i++){
            sleep(2000);
            System.out.println(Thread.currentThread().getName());
        }
    } catch (Exception e) {
    }
}
}

public class App {
public static void main(String[] args) {

    Task t1 = new Task() ;
    Task t2 = new Task() ;

    t1.start();
   try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
    t2.start();

}
}

【讨论】:

    猜你喜欢
    • 2016-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多