如何在线程外停止预定 task/thread

How to stop scheduled task/thread outside of thread

我正在尝试练习和学习更多关于多线程和调度任务的知识。
我编写了一个测试程序来模拟我试图在机器人中实现的调度程序,它的行为方式我并不真正理解。基本上我创建了一个任务并将其安排到 运行 并且我希望它在某些事件后被取消(在这种情况下,当计数 > 5 时)。
它似乎 运行 无限期地即使计数超过 5,但是当我放入一行以休眠主线程或从它打印时,它按我预期的那样工作。

谁能知道为什么会这样?
就好像没有与主线程交互一样,它永远不会遇到条件或者只是忽略它,但是只要我放入一些东西让主线程处理,它也会检查条件。

public class Driver {
  public static void main(String[] args) throws InterruptedException {
    TestScheduler test = new TestScheduler();
    test.startScheduler();
  }
}


public class TestScheduler {
  private static ScheduledExecutorService ses;
  private static int count;

  public TestScheduler(){
    ses = Executors.newScheduledThreadPool(2);
    count = 0;
  }

  public void startScheduler() throws InterruptedException {
    System.out.println("startScheduler() thread: " + Thread.currentThread().getName());

    Runnable testTask = () -> {
      System.out.println(Thread.currentThread().getName() + ": count " + count++);
    };

    System.out.println("Starting test scheduler for 10s");
    ScheduledFuture<?> scheduledFuture = ses.scheduleAtFixedRate(testTask, 5, 1, TimeUnit.SECONDS);
    System.out.println("ScheduledFuture started...");

    while(true){
      // if any of the 2 lines below are uncommented, it works as I'd expect it to...
      //Thread.sleep(1000);
      //System.out.println(Thread.currentThread().getName() + ": count " + count);
      if (count > 5){
        System.out.println(Thread.currentThread().getName() + ": Cancelling scheduled task.");
        scheduledFuture.cancel(true);
        break;
      }
    }
    System.out.println("Ending test scheduler");
  }

这是带有 Thread.sleep 且 println 被注释掉的输出:

startScheduler() thread: main
Starting test scheduler for 10s
ScheduledFuture started...
pool-1-thread-1: count 0
pool-1-thread-2: count 1
pool-1-thread-2: count 2
pool-1-thread-2: count 3
pool-1-thread-2: count 4
pool-1-thread-2: count 5
pool-1-thread-2: count 6
pool-1-thread-2: count 7
pool-1-thread-1: count 8
pool-1-thread-1: count 9
pool-1-thread-1: count 10
...

并取消注释 2 行:

startScheduler() thread: main
Starting test scheduler for 10s
ScheduledFuture started...
main: count 0
main: count 0
main: count 0
main: count 0
pool-1-thread-1: count 0
main: count 1
pool-1-thread-1: count 1
main: count 2
pool-1-thread-1: count 2
main: count 3
pool-1-thread-1: count 3
main: count 4
pool-1-thread-1: count 4
main: count 5
pool-1-thread-1: count 5
main: count 6
main: Cancelling scheduled task.
Ending test scheduler

如果有任何资源可以解释为什么会出现上述情况,并且可能是多线程的介绍,我将不胜感激。

此外,是否有一种理想的方法来处理相关线程之外的取消线程,比如有一个专门用于 checking/managing 条件的方法?

这是由于 race conditions 在访问 count 时发生的。
2个线程在没有任何锁的情况下同时访问这个变量。
您可以使用 AtomicInteger 来克服这个问题:

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

public class Driver {
  public static void main(String[] args) throws InterruptedException {
    TestScheduler test = new TestScheduler();
    test.startScheduler();
  }
}


class TestScheduler {
  private ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);
  private AtomicInteger count = new AtomicInteger(0);

  public void startScheduler() throws InterruptedException {
    System.out.println("startScheduler() thread: " + Thread.currentThread().getName());

    Runnable testTask = () -> {
      System.out.println(Thread.currentThread().getName() + ": count " + count.getAndIncrement());
    };

    System.out.println("Starting test scheduler for 10s");
    ScheduledFuture<?> scheduledFuture = ses.scheduleAtFixedRate(testTask, 5, 1, TimeUnit.SECONDS);
    System.out.println("ScheduledFuture started...");

    while(true){
      if (count.get() > 5){
        System.out.println(Thread.currentThread().getName() + ": Cancelling scheduled task.");
        scheduledFuture.cancel(true);
        break;
      }
    }
    System.out.println("Ending test scheduler");
  }
}

其实是因为多线程使用了不同的cpu核心,所以同一个变量在不同的cpu缓存中保持不同的值,你可以将计数设为volatile解决了 problem.You 可以看到 post http://tutorials.jenkov.com/java-concurrency/volatile.html 如果你对 volatile 感兴趣的话。代码是

package com.test;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 *
 */
public class TestList {

    public static class TestScheduler {
        private static ScheduledExecutorService ses;
        private static volatile int count;

        public TestScheduler() {
            ses = Executors.newScheduledThreadPool(2);
            count = 0;
        }

        public void startScheduler() throws InterruptedException {
            System.out.println("startScheduler() thread: " + Thread.currentThread().getName());

            Runnable testTask = () -> {
                System.out.println(Thread.currentThread().getName() + ": count " + count++);
            };

            System.out.println("Starting test scheduler for 10s");
            ScheduledFuture<?> scheduledFuture = ses.scheduleWithFixedDelay(testTask, 5, 1, TimeUnit.SECONDS);
            System.out.println("ScheduledFuture started...");

            while (true) {
                // if any of the 2 lines below are uncommented, it works as I'd expect it to...
                // Thread.sleep(1000);
                // System.out.println(Thread.currentThread().getName() + ": count " + count);
                if (count > 5) {

                    System.out.println(Thread.currentThread().getName() + ": Cancelling scheduled task.");
                    scheduledFuture.cancel(true);
                    break;
                }
            }
            System.out.println("Ending test scheduler");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestScheduler test = new TestScheduler();
        test.startScheduler();
    }

}