有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程一种使用多线程的短Java程序

我正在尝试编写一个使用多线程的简短Java程序。 我希望主线程每100毫秒打印一次“I”。 子线程每1000毫秒打印一次“测试”

结果应该是这样的

0,1,2,3,4,5,6,7,8,9,Test
10,11,12,13,14,15,16,17,18,19,Test
20,21,22,23,24,25,26,27,28,29,Test
30,31,32,33,34,35,36,37,38,39,Test
40,41,42,43,44,45,46,47,48,49,Test
50,51,52,53,54,55,56,57,58,59,Test
60,61,62,63,64,65,66,67,68,69,Test
70,71,72,73,74,75,76,77,78,79,Test
80,81,82,83,84,85,86,87,88,89,Test
90,91,92,93,94,95,96,97,98,99,Test

但我的结果是这样的

0,1,2,3,4,5,6,7,8,9,Test
10,11,12,13,14,15,16,17,18,Test
19,20,21,22,23,24,25,26,27,Test
28,29,30,31,32,33,34,35,36,Test
37,38,39,40,41,42,43,44,45,Test
46,47,48,49,50,51,52,53,54,Test
55,56,57,58,59,60,61,62,63,Test
64,65,66,67,68,69,70,71,72,Test
73,74,75,76,77,78,79,80,81,Test
82,83,84,85,86,87,88,89,90,91,Test
92,93,94,95,96,97,98,99,

这是我的全部代码:

public class Main {
    public static void main(String[] args){
        Main.repeatTask(10, 1000);

        //print i, then sleep 100ms
        for(int i=0;i<100;i++){
            System.out.print(i + ",");
            try{
                Thread.sleep(100);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    //sleep (time)ms then print "Test"
    public static void repeatTask(int m, int time) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < m; i++) {
                    try {
                        Thread.sleep(time);
                        System.out.println("Test");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

我不能改变主要的方法。如何更改我的任务方法


共 (1) 个答案

  1. # 1 楼答案

    这两个线程是独立的,也可能运行在不同的JVM中,因此没有任何东西可以保证另一个线程中的sleep()+唤醒1000ms将恰好匹配主线程100ms睡眠的10倍

    如果确实无法更改main方法,则可以创建自己的PrintStream实现(这是System.out中用于打印的内容):

    public static class CustomPrintStream extends PrintStream {
    
        private final AtomicReference<CountDownLatch> latch;
        private final int repetitions;
    
        public CustomPrintStream(int repetitions) {
            super(System.out);
            this.repetitions = repetitions;
            this.latch = new AtomicReference<>(new CountDownLatch(repetitions));
        }
    
        @Override
        public void print(String s) {
            super.print(s);
            latch.get().countDown();
        }
    
        public void waitForLatch() throws InterruptedException {
            latch.get().await();
        }
    
        public void resetLatch() {
            latch.set(new CountDownLatch(repetitions));
        }
    }
    

    上述实现使用的System.out与JVM中默认的System.out相同,只是:

    1. 包含大量重复(在您的案例中10
    2. 包含对CountDownLatch的原子引用,该引用在10处初始化,每次接触0时在10处重新初始化

    因此,您可以如下修改repeatTask方法:

    public static void repeatTask(int m, int time) { //<  note: you don't need time any longer
        CustomPrintStream customPrintStream = new CustomPrintStream(m); //<  create your custom PrintStream with your number of repetitions
        System.setOut(customPrintStream); //<  set it as the out for the JVM
        new Thread(() -> {
            for (int i = 0; i < m; i++) {
                try {
                    customPrintStream.waitForLatch(); //<  you wait for the countdown to 10 (the countdown is performed each time the main thread prints a number)
                    System.out.println("Test"); //<  hence you print Test
                    customPrintStream.resetLatch(); //<  and reset the countdown to 10
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    

    样本输出:

    0,1,2,3,4,5,6,7,8,9,Test
    10,11,12,13,14,15,16,17,18,19,Test
    20,21,22,23,24,25,26,27,28,29,Test
    30,31,32,33,34,35,36,37,38,39,Test
    40,41,42,43,44,45,46,47,48,49,Test
    50,51,52,53,54,55,56,57,58,59,Test
    60,61,62,63,64,65,66,67,68,69,Test
    70,71,72,73,74,75,76,77,78,79,Test
    80,81,82,83,84,85,86,87,88,89,Test
    90,91,92,93,94,95,96,97,98,99,Test
    

    注意,这里我不是在等待1000毫秒,而是在等待主线程从System.out.print()经过10次。 这可以保证,如果JVM速度减慢,因此10次的总和不是1000ms,而是1200ms,那么第二个线程仍将等待调用10次后再打印Test

    Disclaimer: I think the above solution is overkill. If it's for a conceptual demonstration then fine, but if it was real production code I would discuss with the owner of main in order to adapt the implementations without having to override the standard PrintStream