Java 中的多线程。倒计时和计时器同时进行

Multi threading in Java. Countdown and timer both at once

计时器和倒计时方法在单独 运行 时都有效。它们也作为两个单独的线程工作,但是线程序列的优先级导致倒计时和计时器(向上计数)不准确。

这是示例 output:cookie 10 饼干 9 饼干 8 秒 1 饼干 7 秒 2 秒 3 饼干 6 秒 4 饼干 5 秒 5 饼干 4 饼干 3 秒 6 饼干 2 秒 7 饼干 1 秒 8 秒 9 秒 10 秒 11 秒 12 秒 13 秒 14 秒 15 秒 16 秒 17 秒 18 秒 19

一旦 cookie 定时器完成,定时器应该继续,但是当定时器和倒计时都处于活动状态时,线程应该以秒为单位跟踪时间而不会失去准确性。目前我依靠控制台来显示此计数,但我将在图形界面中对其进行编程,向用户显示倒计时和计时器。或许线程的执行需要在它们之间交替,以便计时器能够平等地进行,此外,它们应该同步,这样一个线程就无法在没有另一个线程的情况下继续执行。有关实施的任何提示。谢谢

public class 控制台 {

     long lastTime;
     boolean counting = true;
     boolean cCounting = true;
     long seconds = 0;
     long delta = 0;
     volatile int startNumber = (int) Level.cookieTime / 1000;
    Thread countDown;
    Thread time;

    public Console() {

        counting = Game.enter;

        cookieCountDown();
        timer();
        lastTime = System.currentTimeMillis();
    }

    public void timer() {

        time = new Thread(new Runnable() {
            @Override
            public void run() {
                seconds = 0;


                while (true) {
                    try {
                        Thread.sleep(0);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    counting = Game.enter;

                    while (counting) {

                        long now = System.currentTimeMillis();
                        delta = now - lastTime;

                        if (delta >= 1000) {

                            delta = 0;
                            lastTime = System.currentTimeMillis();
                            System.out.println("seconds " + seconds); //Print seconds
                            seconds++;

                            if (!counting){
                                System.out.println("stoped"  + counting);
                                //time.stop();
                              //  return;
                            }
                        }
                    }
                }
            }

        });
        time.start();

    }

    public void cookieCountDown() {
        countDown = new Thread(new Runnable() {
            @Override
            public void run() {
                Player.cCounting = true;

                while (startNumber != 0) {
                    startNumber = (int) Level.cookieTime / 1000;
                    cCounting = Game.enter;

                    while (startNumber > 0 && cCounting) {
                        long now = System.currentTimeMillis();
                        delta = now - lastTime;

                        if (delta >= 1000) {
                            delta = 0;
                            lastTime = System.currentTimeMillis();
                            System.out.println("cookie " + startNumber);// print countdown;
                            startNumber--;

                            if (!Player.cCounting) {
                                Player.cCounting = true;
                                return;
                            }
                        }
                    }
                }
            }
        });
        countDown.start();

        if (startNumber == 0 || !Player.cCounting) {

            Player.cCounting = true;
            startNumber = (int) Level.cookieTime / 1000;

        }
    }


    public void setCounting(boolean counting) {
        this.counting = counting;
    }
}

线程中的自由循环给 CPU 带来了相当大的负载,这可能会对其他线程产生不利影响。请记住,更多线程并不总是意味着可以更快地完成更多工作。

我们需要的是一些方法来 "yield" 时间,这样 CPU 可以更好地安排其他线程。

由于您只对秒精度感兴趣,因此使用 sleep 半秒是一个不错的起点。这大大减少了每个线程在 CPU.

上所需的时间。

就个人而言,在处理像这样基于时间的解决方案时,我更喜欢使用 date/time API,因为它通常会产生更好、更可靠的解决方案,但我就是这样。

下面的例子简单地启动了 10 个线程,每个线程有 5 秒的超时时间。每个线程在运行通过规定的逻辑

之前休眠半秒
import java.time.Duration;
import java.time.Instant;
import java.util.Random;

public class Test {

    public static void main(String[] args) throws InterruptedException {
        new Test();
    }

    public Test() throws InterruptedException {
        Random rnd = new Random();
        for (int index = 0; index < 10; index++) {
            Thread t = new Thread(new Timeout(5, "Cookie " + index));
            t.start();
        }

        Thread.sleep(500);
    }

    public class Timeout implements Runnable {

        private Duration duration;
        private Instant startTime;
        private String label;

        public Timeout(int count, String label) {
            duration = Duration.ofSeconds(count);
            this.label = label;
        }

        @Override
        public void run() {
            long time = Long.MAX_VALUE;
            try {
                startTime = Instant.now();
                while (true) {
                    Duration runTime = Duration.between(startTime, Instant.now());
                    Duration remainingTime = duration.minus(runTime);
                    // You could also use remainingTime.getSeconds() == 0, but it
                    // depends on your desired level of accuracy
                    if (remainingTime.isNegative()) {
                        System.out.println("Out of time");
                        return;
                    } else {
                        if (time != remainingTime.getSeconds()) {
                            time = remainingTime.getSeconds();
                            System.out.println(label + " " + duration.getSeconds() + "/" + time);
                        }
                    }
                    Thread.sleep(500);
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}

这产生了类似于...的输出

Cookie 3 5/5
Cookie 4 5/5
Cookie 0 5/5
Cookie 1 5/5
Cookie 2 5/5
Cookie 6 5/5
Cookie 9 5/5
Cookie 5 5/5
Cookie 7 5/5
Cookie 8 5/5
Cookie 1 5/4
Cookie 5 5/4
Cookie 7 5/4
Cookie 6 5/4
Cookie 2 5/4
Cookie 0 5/4
Cookie 3 5/4
Cookie 4 5/4
Cookie 8 5/4
Cookie 9 5/4
//...
Cookie 5 5/1
Cookie 3 5/1
Cookie 0 5/1
Cookie 7 5/1
Cookie 1 5/1
Cookie 2 5/1
Cookie 6 5/1
Cookie 8 5/1
Cookie 4 5/1
Cookie 9 5/1
Cookie 5 5/0
Cookie 7 5/0
Cookie 4 5/0
Cookie 8 5/0
Cookie 0 5/0
Cookie 2 5/0
Cookie 3 5/0
Cookie 1 5/0
Cookie 6 5/0
Cookie 9 5/0
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time

另一种解决方案可能是使用单线程和 "timers" 的 List。该线程将 "tick" 计时器,这将允许他们确定他们已经 运行 了多长时间以及他们是否已经过期,例如...

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {

    public static void main(String[] args) throws InterruptedException {
        new Test();
    }

    public Test() throws InterruptedException {
        List<Timeout> timers = new ArrayList<>(10);
        for (int index = 0; index < 10; index++) {
            timers.add(new Timeout(5, "Cookie " + index));
        }

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Iterator<Timeout> it = timers.iterator();
                    while (it.hasNext()) {
                        Timeout timer = it.next();
                        timer.tick();
                        if (timer.isTimedOut()) {
                            it.remove();
                        }
                    }
                    Thread.yield();
                    if (timers.isEmpty()) {
                        return;
                    }
                }
            }
        });
        t.start();
        Thread.sleep(500);
    }

    public class Timeout {

        private Duration duration;
        private Instant startTime;
        private String label;

        private Long lastTime;
        private boolean timedOut;

        public Timeout(int count, String label) {
            duration = Duration.ofSeconds(count);
            this.label = label;
        }

        public boolean isTimedOut() {
            return timedOut;
        }

        public void tick() {
            if (timedOut) {
                return;
            }
            if (startTime == null) {
                startTime = Instant.now();
            }
            Duration runTime = Duration.between(startTime, Instant.now());
            Duration remainingTime = duration.minus(runTime);
            // You could also use remainingTime.getSeconds() == 0, but it
            // depends on your desired level of accuracy
            if (remainingTime.isNegative()) {
                System.out.println("Out of time");
                timedOut = true;
            } else {
                if (lastTime == null || lastTime != remainingTime.getSeconds()) {
                    lastTime = remainingTime.getSeconds();
                    System.out.println(label + " " + duration.getSeconds() + "/" + lastTime);
                }
            }
        }
    }
}

我什至可能会在 return 计时器的 "duration"、运行 宁时间和剩余时间量中添加一些方法,但这就是我。

这样做的缺点是,如果 "main" 线程花费的时间太长,计时器可能会在下一个检查周期之前到期。在上面的示例中,我基本上允许线程尽可能快地 运行(我确实添加了 yield,但仍然不是我最喜欢做的事情)并简单地循环遍历 "timer"s 直到所有计时器都已过期。

哪种解决方案更好?取决于你的情况。就个人而言,我倾向于针对单个快速 运行ning 线程(我倾向于使用 Thread.sleep(5),但那只是我),它可以迭代一系列 "things to be done"。对于上面的例子,因为我们依赖于基于时间的解决方案(而不是计数器),即使我们有一些滞后,我们仍然会得到(合理的)准确的结果