简体   繁体   中英

How to stop scheduled task/thread outside of thread

I'm trying to practice and learn more about multi-threading and scheduling tasks.
I wrote a test program that mimics a scheduler I'm trying to implement in a bot and its behaving in a way I don't really understand. Basically I created a task and scheduled it to run and I want it to be canceled after some event (in this instance, when count > 5).
It seems to run indefinitely even though the count is over 5, but when I put in a line to sleep the main thread or print from it, it works as I'd expect it to.

Can someone why this is the case?
It's as if like if there is no interaction with the main thread, it never hits the condition or just ignores it, but as soon as I put something in for the main thread to process, it checks the condition as well.

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

Here is the output with Thread.sleep and println commented out:

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
...

And with the 2 lines uncommented:

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

If there are any resources to look at that can explain why the above scenario occurs and possibly one for an intro to multithreading I'd appreciate it.

Also, is there an ideal way to handle cancelling threads outside of the thread in question, like having one dedicated to checking/managing the conditions?

Its happening due to race conditions while accessing count .
2 threads are accessing this variable at the same time without any locks.
You can use an AtomicInteger to overcome this:

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

actually the reason is that the multi thread has used the differect cpu core,so the same variable keep different value in the different cpu cache,you could just make the count to volatile to solve the problem.You could see the post http://tutorials.jenkov.com/java-concurrency/volatile.html if you are interesting about the volatile.The code is that

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM