我正在尝试练习和学习有关多线程和调度任务的更多信息。
我编写了一个测试程序,该程序模仿了我正在尝试在机器人中实现的调度程序,并且其行为方式我不太了解。基本上,我创建了一个任务并将其安排为运行,并且我希望在某个事件(在这种情况下,当count> 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
...
并且没有注释两行:
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
如果有任何资源可供参考,可以解释为什么发生上述情况,并且可能是多线程介绍的一种情况。
还有,有没有理想的方法来处理有问题的线程之外的取消线程,例如专门负责检查/处理条件?
答案 0 :(得分:1)
由于访问count
时发生race conditions而导致。
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");
}
}
答案 1 :(得分:-1)
实际上原因是多线程使用了不同的cpu内核,因此相同的变量在不同的cpu缓存中保持不同的值,您只需将计数变为volatile即可解决该问题。您可以看到帖子{ {3}}如果您对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();
}
}