如何在线程外停止预定 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();
}
}
我正在尝试练习和学习更多关于多线程和调度任务的知识。
我编写了一个测试程序来模拟我试图在机器人中实现的调度程序,它的行为方式我并不真正理解。基本上我创建了一个任务并将其安排到 运行 并且我希望它在某些事件后被取消(在这种情况下,当计数 > 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();
}
}