簡體   English   中英

如何在線程外停止計划任務/線程

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

這是 output 與 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

如果有任何資源可以解釋為什么會發生上述情況,並且可能有一個用於介紹多線程的資源,我將不勝感激。

此外,是否有一種理想的方法來處理相關線程之外的取消線程,比如有一個專門用於檢查/管理條件的方法?

它是由於訪問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緩存中保持不同的值,你可以把count設置為volatile來解決這個問題。你可以看到帖子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();
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM