Java中的多线程。倒计时和计时器都是一次性的

时间:2018-06-11 22:46:31

标签: java multithreading runnable

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

以下是示例输出: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 Console {

     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;
    }
}

1 个答案:

答案 0 :(得分:1)

线程中的空转循环会给CPU带来相当大的负担,这会对其他线程产生负面影响。请记住,更多线程并不总是意味着更快地完成更多工作。

需要的是一些“产生”时间的方法,因此CPU可以更好地安排其他线程。

由于您只对第二个准确度感兴趣,因此使用半秒sleep是一个很好的起点。这大大减少了每个线程在CPU上所需的时间。

就个人而言,在处理这样的基于时间的解决方案时,我更喜欢使用日期/时间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

另一种解决方案可能是使用单个线程和List个“定时器”。该线程将“勾选”定时器,这将允许他们确定他们已经运行了多长时间并且它们已经过期或未过期,例如......

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);
                }
            }
        }
    }
}

我甚至可以添加几种方法来返回计时器的“持续时间”,运行时间和剩余时间,但那就是我。

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

哪种解决方案更好?取决于您的情况。就个人而言,我倾向于瞄准一个快速运行的线程(我倾向于使用Thread.sleep(5),但这只是我),它可以迭代一系列“要完成的事情”。通过上面的例子,因为我们依赖基于时间的解决方案(而不是计数器),即使我们有一些滞后,我们仍然得到(合理)准确的结果