詳細な Java 同時プログラミングとタスク スケジューリング

パート 1: 同時プログラミングの基礎

同時プログラミングについて説明する前に、まずいくつかの基本的な概念と原則を理解する必要があります。

スレッドとは何ですか

スレッドは、コンピュータ オペレーティング システムの基本的な実行単位の 1 つです。これはプロセスの一部であり、オペレーティング システムのスケジューリングの基本単位です。プロセスには複数のスレッドを含めることができ、それぞれが独自のコードを実行しますが、それらはプロセスのメモリとその他のリソースを共有します。

一般に、スレッドはプロセスよりも軽量で、より速く作成および破棄でき、システム リソースをより効率的に使用できます。異なるスレッドを同時に実行できるため、複数のタスクを同時に実行できます。スレッドは単一の CPU 上で実行することも、複数の CPU 上で同時に実行することもできるため、並列コンピューティングが可能になります。

スレッドは同時プログラミングにおける重要な概念であり、マルチタスク アプリケーションの作成によく使用されます。たとえば、Web サーバーでは、各リクエストを別個のスレッドに割り当てて処理できるため、サーバーは複数のリクエストを同時に処理できます。

Java では、スレッドは Thread クラスを通じて実装されます。スレッドは並行して実行することも、連携してタスクを完了することもできます。

同時実行とは何ですか

Java では、同時実行性とは、一定期間内に同じプログラム内で複数のスレッドが実行されることを指します。複数のスレッドが同時に実行されると、一部のリソース (メモリやファイルなど) を共有する可能性があり、データ競合やその他の問題が発生する可能性があります。Java には、複数のスレッド間の実行を調整および制御して、共有リソースに適切にアクセスできるようにするためのメカニズムがいくつか用意されています。

同期とは何ですか

同期は、複数のスレッドが共有リソースに同時にアクセスしないようにするために使用されるメカニズムです。Java では、synchronized キーワードを使用してコード ブロックを同期済みとしてマークできるため、常に 1 つのスレッドだけがコード ブロックにアクセスできるようになります。synchronized キーワードは、インスタンス メソッド、静的メソッド、およびコード ブロックで使用できます。

ミューテックスとは何ですか

相互排他は、常に 1 つのスレッドだけが共有リソースにアクセスできるようにするために使用されるメカニズムです。Java では、ロック (Lock) オブジェクトを使用して相互排他を実現できます。Lock オブジェクトは、1 つのスレッドだけがロックを保持できることを保証でき、ロックを保持しているスレッドがロックを解放した後でのみ、他のスレッドがロックを取得して共有リソースにアクセスできるようになります。synchronized キーワードとは異なり、Lock オブジェクトは、リエントラント ロックやフェア ロックなどのより高度な機能を提供できます。

パート II: JDK ネイティブ スレッドとタスク スケジューリング

Java は、スレッド、ロック、セマフォ、ブロッキング キューなどを含む豊富な同時プログラミング API を提供します。このセクションでは、JDK のネイティブ スレッドとタスク スケジューリングについて詳しく説明します。

スレッドを作成する

Java では、スレッドを作成する方法が 2 つあります。1 つは Thread クラスを継承する方法、もう 1 つは Runnable インターフェイスを実装する方法です。

public class MyThread extends Thread {
    public void run() {
        // 线程执行的代码
    }
}

public class MyRunnable implements Runnable {
    public void run() {
        // 线程执行的代码
    }
}

// 创建线程并启动
MyThread thread = new MyThread();
thread.start();

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();

スレッドの状態

Java では、スレッドには新規、実行中、ブロック済み、待機中、時間指定待機、終了などの複数の状態があります。

  • NEW: 新しい状態。スレッド オブジェクトは作成されましたが、スレッドを開始するために start() メソッドが呼び出されていません。
  • RUNNABLE: 実行状態。スレッドは実行中か、CPU タイム スライスを待機しています。
  • BLOCKED: ブロックされた状態。スレッドは排他ロックの取得を待機しているか、I/O 操作が完了するのを待機しています。
  • WAITING: 待機状態。スレッドは他のスレッドからの通知を待っており、無期限の待機状態にあります。
  • TIMED_WAITING: 時間制限された待機状態。スレッドは他のスレッドからの通知を待機していますが、最大でも一定時間待機します。
  • TERMINATED: 終了状態。スレッドは実行を完了したか、中断されました。

スレッド状態図:

+----------------+     +----------------+     +-----------------+
| NEW            | --> | RUNNABLE       | --> | BLOCKED         |
|                |     |                |     |                 |
|                |     |                |     | waiting for     |
|                |     |                |     | monitor lock    |
+----------------+     +----------------+     +-----------------+
       |                      |                           |
       |                      |                           |
       |                      |                           |
       |                      |                           |
       V                      V                           V
+----------------+     +----------------+     +-----------------+
| TERMINATED     | <-- | TIMED_WAITING  | <-- | WAITING         |
|                |     |                |     |                 |
|                |     |                |     | waiting for     |
|                |     |                |     | another thread  |
|                |     |                |     | or I/O to finish|
+----------------+     +----------------+     +-----------------+

スレッドの同期

スレッド同期は、複数のスレッド間で共有リソースへのアクセスの正しい順序を保証するメカニズムです。Java は、 synchronized、Lock、Semaphoreなど、スレッド同期を実現するさまざまな方法を提供します。

同期した

同期は、Java で最も一般的に使用されるスレッド同期メカニズムであり、同時に 1 つのスレッドのみが共有リソースにアクセスできるようにします。synchronized はメソッドまたはコード ブロックで使用でき、オブジェクトのロックを取得することで同期を実現します。

public class Counter {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
    public synchronized void decrement() {
        count--;
    }
}

上記のコードでは、increment() メソッドと decrement() メソッドは同期しており、Counter オブジェクトのロックを取得し、count++ または count__ 操作を実行します。これにより、複数のスレッドによる count 変数へのアクセスが安全になります。

ロック

ロックは、Java で一般的に使用されるもう 1 つのスレッド同期メカニズムであり、より多くの機能と柔軟性を提供します。ロックは同期の実装に使用でき、待機/通知メカニズムの実装にも使用できます。同期とは異なり、Lock は Java 言語レベルのキーワードではなく、Java クラスです。Lock は、lock()、tryLock()、lockInterruptibly()、newCondition() など、スレッド同期を実現するさまざまなメソッドを提供します。

public class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    public void decrement() {
        lock.lock();
        try {
            count--;
        } finally {
            lock.unlock();
        }
    }
}

上記のコードでは、increment() メソッドと decrement() メソッドは Lock を使用して実装されています。これらはロック オブジェクトを取得し、count++ または count_- 操作を実行します。同期とは異なり、Lock を使用するには明示的にロックを取得および解放する必要があります。

セマフォ

セマフォは Java で一般的に使用される同期ツールで、同時に共有リソースにアクセスするスレッドの数を制御できます。Semaphore には、ライセンスの取得と解放に使用される、よく使用される 2 つのメソッドacquire () と release () が用意されています。

以下は、複数のスレッドがアクセスする必要がある共有リソースがあるセマフォを使用する簡単な例です。ただし、同時にリソースにアクセスできるスレッドは 2 つだけです。

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    // 定义 Semaphore,初始许可数为 2
    private static final Semaphore sem = new Semaphore(2);

    public static void main(String[] args) {
        // 创建 5 个线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    // 尝试获取许可
                    sem.acquire();
                    System.out.println("Thread " + Thread.currentThread().getName() + " acquired permit.");
                    // 模拟线程执行一段时间
                    Thread.sleep(2000);
                    System.out.println("Thread " + Thread.currentThread().getName() + " releasing permit.");
                    // 释放许可
                    sem.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

上記の例では、セマフォの初期許可番号は 2 です。これは、最大 2 つのスレッドのみが共有リソースに同時にアクセスできることを意味します。各スレッドはまず、acquire() メソッドを呼び出して許可の取得を試みます。許可が取得できない場合、許可が取得できるまでスレッドはブロックされます。スレッドが許可を取得すると、作業をシミュレートするコードを実行し、許可を解放します。スレッドが許可を解放すると、別のスレッドが許可を取得して実行を継続する機会が得られます。セマフォはアクセス許可の数を制限するため、共有リソースに同時にアクセスできるスレッドは 2 つだけです。

スレッドプール

スレッド プールは一般的に使用されるスレッド管理方法で、スレッドの頻繁な作成と破棄によるオーバーヘッドを回避し、スレッドの数を制御してシステムの安定性と効率を確保できます。Java では、スレッド プール機能を実装するための ThreadPoolExecutor クラスが提供されています。

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            Runnable task = new Task();
            executor.execute(task);
        }
        executor.shutdown();
    }
}

上記のコードでは、サイズ 5 のスレッド プールが作成され、10 個のタスクが送信されます。各タスクは Runnable インターフェイスを実装したクラスであり、executor.execute(task) メソッドの実行後、スレッド プールはタスクを実行するスレッドを自動的にスケジュールします。executor.shutdown() メソッドの実行後、スレッド プールはシャットダウンし、すべてのタスクが完了するまで待機します。

タスクのスケジュール設定

タスクのスケジューリングとは、指定された時間または条件で指定されたタスクを実行することを指します。Java は、Timer、ScheduledExecutorService など、さまざまなタスク スケジューリング メソッドを提供します。

タイマー

Timer は Java に付属するタスク スケジューラで、指定した時間に指定したタスクを実行できます。Timer には、1 回限りのタスクと周期的なタスクの実行に使用される 2 つのメソッド、schedule() とscheduleAtFixedRate() が用意されています。

public class TimerDemo {
    public static void main(String[] args) {
        TimerTask task = new TimerTask() {
            public void run() {
                System.out.println("task is running");
            }
        };
        Timer timer = new Timer();
        timer.schedule(task, 5000);
    }
}

上記のコードでは、Timer オブジェクトと TimerTask オブジェクトが作成され、timer.schedule(task, 5000) メソッドが呼び出され、5 秒後にタスクが実行されます。Timer は、周期的なタスクを実行するためのスケジュール AtFixedRate() などの他のメソッドも提供します。

ScheduledExecutorService

ScheduledExecutorService は Java で推奨されるタスク スケジューラで、Timer よりも柔軟で信頼性の高いタスク スケジューリング方法を提供します。ScheduledExecutorService には、1 回限りのタスクと周期的なタスクの実行に使用される 2 つのメソッド、schedule() とscheduleAtFixedRate() が用意されています。

public class ScheduledExecutorDemo {
    public static void main(String[] args) {
        Runnable task = new Task();
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.schedule(task, 5, TimeUnit.SECONDS);
        executor.shutdown();
    }
}

上記のコードでは、ScheduledExecutorService オブジェクトとタスク オブジェクトが作成され、executor.schedule(task, 5, TimeUnit.SECONDS) メソッドが呼び出され、5 秒後にタスクが実行されます。ScheduledExecutorService は、周期的なタスクを実行するための、scheduleWithFixedDelay() などの他のメソッドも提供します。

サードパーティのクラス ライブラリを使用する

JDK 独自のスレッドとタスクのスケジューリング機能に加えて、Java には、同時プログラミングとタスクのスケジューリングを実装するために使用できる優れたサードパーティ ライブラリも多数あります。一般的なサードパーティのクラス ライブラリには次のものがあります。

  • Apache Commons Lang
  • グアバ
  • 石英
  • 春のタスク

ここでは、Quartz と Spring Task を例として、これら 2 つのクラス ライブラリを使用して同時プログラミングとタスク スケジューリングを実装する方法を紹介します。

石英

Quartz は、オープンソースのタスク スケジューリング フレームワークであり、より豊富で柔軟なタスク スケジューリング機能を提供し、分散タスク スケジューリングとクラスター タスク スケジューリングをサポートします。Quartz は、電子メールの送信やデータのバックアップなどの複雑なタスクだけでなく、1 回限りのタスクや周期的なタスクの実行にも使用できます。

クォーツの特徴は次のとおりです。

  1. 高い柔軟性: Quartz は、さまざまなトリガー タイプ (単純なトリガー、cron トリガーなど) に従ってタスクをスケジュールでき、タスクの実行優先度、実行時間、時間などの属性も構成できます。
  2. 高い信頼性: Quartz は、タスクの実行中に障害が発生した場合に自動的に処理および回復できるエラー処理および回復メカニズムを提供します。
  3. 分散およびクラスターのサポート: Quartz は分散タスク スケジューリングとクラスター タスク スケジューリングをサポートしており、複数のマシンにデプロイしてタスクの信頼性とスケーラビリティを向上させることができます。

Quartz はスケジュールされたタスクをスケジュールします

以下は、タスクのスケジューリングに Quartz を使用する Java の例です。これは、Quartz を使用して時間指定されたタスクをスケジュールし、10 秒ごとに文を出力します。

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;

public class QuartzDemo implements Job {
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("Hello Quartz! " + new Date());
    }

    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 创建 Scheduler 工厂
        StdSchedulerFactory factory = new StdSchedulerFactory();
        // 创建 Scheduler
        Scheduler scheduler = factory.getScheduler();
        // 创建 JobDetail
        JobDetail jobDetail = JobBuilder.newJob(QuartzDemo.class).build();
        // 创建触发器
        Trigger trigger = TriggerBuilder.newTrigger()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever())
                .build();
        // 将任务和触发器加入 Scheduler
        scheduler.scheduleJob(jobDetail, trigger);
        // 启动 Scheduler
        scheduler.start();
        // 让程序休眠 60 秒
        Thread.sleep(60000);
        // 停止 Scheduler
        scheduler.shutdown();
    }
}

 上記の例では、まず、Quartz の Job インターフェイスを実装する Job クラス (QuartzDemo) を作成し、実行するタスクを定義するexecute() メソッドを書き換えます。

次に、Quartz の SchedulerFactory を使用して Scheduler を作成し、JobDetail オブジェクトと Trigger オブジェクトを作成します。JobDetail は実行する必要があるジョブを定義し、Trigger はジョブのトリガー条件を定義します。

最後に、Scheduler に JobDetail と Trigger を追加し、Scheduler を起動します。プログラムが 60 秒間スリープした後、スケジューラーを停止します。プログラムの実行中、ジョブで定義されたタスクが 10 秒ごとに実行されます。

Quartz を使用した Cron 式

スケジュールされたタスクに加えて、Quartz は次のような他のタイプのタスク スケジューリングも提供します。

  1. Cron 式トリガー: Linux のような Cron 式を使用してタスクの実行時間を定義し、タスクの実行時間と頻度を柔軟に定義できます。
  2. カレンダー トリガー: カレンダーを使用してタスクの実行時間を定義し、特定の時間を除外してタスクをスケジュールします。
  3. リスナー: Quartz は、タスクの実行の前後、またはトリガーの起動の前後に特定のロジックを実行できるさまざまなリスナーを提供します。
  4. クラスター タスクのスケジューリング: Quartz は、複数の Scheduler インスタンス間のクラスター タスクのスケジューリングをサポートしており、それによりタスクの信頼性とスケーラビリティが向上します。
  5. 分散タスク スケジューリング: Quartz は分散タスク スケジューリングもサポートしており、タスクをリモート ノードにスケジュールして実行できます。

以下は、Quartz の Cron 式トリガーを使用したタスク スケジューリングの Java の例です。これは、Quartz を使用して、毎日午前 9 時に 1 回実行される時間指定タスクをスケジュールします。

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;

public class QuartzDemo implements Job {
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("Hello Quartz! " + new Date());
    }

    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 创建 Scheduler 工厂
        StdSchedulerFactory factory = new StdSchedulerFactory();
        // 创建 Scheduler
        Scheduler scheduler = factory.getScheduler();
        // 创建 JobDetail
        JobDetail jobDetail = JobBuilder.newJob(QuartzDemo.class).build();
        // 创建触发器
        Trigger trigger = TriggerBuilder.newTrigger()
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?"))
                .build();
        // 将任务和触发器加入 Scheduler
        scheduler.scheduleJob(jobDetail, trigger);
        // 启动 Scheduler
        scheduler.start();
        // 让程序休眠 60 秒
        Thread.sleep(60000);
        // 停止 Scheduler
        scheduler.shutdown();
    }
}

上の例では、Quartz の CronScheduleBuilder を使用して、毎日午前 9 時にタスクを起動する Cron 式トリガーを作成します。

Quartz の Cron 式について詳しく説明する

Quartz の Cron 式は、タスクのスケジュール時間を定義するために使用される式であり、Linux に似た Cron 構文を使用してタスクの実行時間と頻度を定義します。Cron 式の構文は非常に複雑ですが、いくつかの基本的な構文規則を知っていれば十分です。単純な Cron 式の例を次に示します。

0 0 0/1 * * ?   // 每小时执行一次

上記の Cron 式は、秒、分、時間、日、月、週を表す 6 つのフィールドで構成されています。各フィールドの構文規則は次のとおりです。

  1. 秒: 0 ~ 59、オプション。
  2. 分: 0 ~ 59、指定する必要があります。
  3. 時間: 0 ~ 23、指定する必要があります。
  4. 日 (日付): 1 ~ 31 を指定する必要があります。
  5. 月:112またはJANDECのいずれかを指定する必要があります。
  6. 曜日: 07 または SUNSAT (0 と 7 は両方とも日曜日を表します)、いずれかを指定する必要があります。

このうち、月と週の 2 つのフィールドは相互に排他的です。つまり、タスクは指定された月または指定された週にのみ実行できます。

基本的な文法規則に加えて、Cron 式は、一般的に使用される期間や論理条件を表すために使用されるいくつかの特殊な記号やキーワードもサポートしています。一般的に使用される特殊記号とキーワードを次に示します。

  1. アスタリスク (*): 任意の値を示し、任意のフィールドで使用できます。
  2. 疑問符 (?): 値が指定されておらず、日フィールドと週フィールドで使用できることを示します。
  3. スラッシュ (/): 増分を指定するために使用されます。たとえば、*/5 は 5 単位ごとに実行することを意味します。
  4. カンマ (,): 複数の値を指定する場合に使用します。たとえば、1,2,3 は、1、2、3 の 3 つの値すべてを実行できることを意味します。
  5. ハイフン(-):範囲を指定する場合に使用します。たとえば、10-15 は、10 ~ 15 の範囲内の値を実行できることを意味します。

Cron 式のサンプルをいくつか示します。

  • :​0 0 0/1 * * ?​ 1時間ごとに実行します。
  • :​0 0 12 ? * MON-FRI​平日の正午に 1 回実行します。
  • :​0 0 1 ? * SAT#3​毎月第 3 土曜日の午前 1 時に実行されます。

cron 式の構文は非常に複雑であり、特定の要件に従って記述する必要があるため、学習および使用については公式ドキュメントを参照することをお勧めします。

春のタスク

Spring Task は、Spring Framework のアノテーションベースのタスク スケジューリング フレームワークであり、シンプルなタスク スケジューリング機能を提供し、1 回限りのタスクと繰り返しのタスクをサポートします。Spring Task の主なコンポーネントは、タスク スケジューラ (TaskScheduler) とタスク エグゼキュータ (TaskExecutor) です。タスク スケジューラはタスクのスケジュール管理を担当し、タスク エグゼキュータはタスクの実行を担当します。

Spring Task の中核となる機能は次のとおりです。

  1. スケジュールされたタスクと定期的なタスクのサポート: Spring Task は、固定遅延、固定レート、Cron 式などのさまざまなタスク トリガー メソッドをサポートします。
  2. 非同期実行のサポート: Spring Task は、メインスレッドをブロックすることなく、別のスレッドでのタスクの実行をサポートします。
  3. タスク フィルタリングのサポート: Spring Task は、タスク名、グループ名、および Cron 式に基づいたタスク フィルタリングをサポートします。

固定遅延タスク、固定レート タスク、および Cron 式タスク

以下は、単純な Spring タスクのサンプル コードです。

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyTask {

    @Scheduled(fixedDelay = 1000)
    public void myFixedDelayTask() {
        System.out.println("Fixed delay task - " + System.currentTimeMillis());
    }

    @Scheduled(fixedRate = 2000)
    public void myFixedRateTask() {
        System.out.println("Fixed rate task - " + System.currentTimeMillis());
    }

    @Scheduled(cron = "0/5 * * * * ?")
    public void myCronTask() {
        System.out.println("Cron task - " + System.currentTimeMillis());
    }
}

上記のコードは、MyTask という Spring タスクを定義します。これには、固定遅延タスク、固定レート タスク、および Cron 式タスクを定義するための 3 つのメソッドが含まれています。これらのメソッドはすべて、Spring Task によって提供される @Scheduled アノテーションを使用して、タスクのトリガー方法を指定します。

このうち、 @Scheduled アノテーションの fixDelay 属性と fixRate 属性は、それぞれ固定遅延タスクと固定レート タスクのトリガー間隔をミリ秒単位で指定するために使用されます。cron 属性は、Cron 式タスクのトリガー時間を指定するために使用されます。上の例では、固定遅延タスクと固定レート タスクはそれぞれ 1 秒と 2 秒ごとに実行され、cron 式タスクは 5 秒ごとに実行されます。

Spring Task機能を有効にするには、Spring Bootアプリケーションのメインクラスに @EnableScheduling アノテーションを追加する必要があることに注意してください。このアノテーションにより、アプリケーションの起動時に Spring Task を自動的に有効にすることができます。

カスタム タスク スケジューラとタスク エグゼキュータ

Spring Task が提供する TaskScheduler インターフェイスと TaskExecutor インターフェイスを使用して、タスク スケジューラとタスク エグゼキュータをカスタマイズすることもできます。これにより、タスクのスケジュール設定とタスクの実行をより柔軟に管理できるようになります。以下は、TaskScheduler インターフェイスと TaskExecutor インターフェイスを使用してタスク スケジューラとタスク エグゼキュータをカスタマイズするサンプル コードです。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;

@Configuration
public class MyTaskConfig {

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10);
        scheduler.setThreadNamePrefix("my-task-scheduler-");
        return scheduler;
    }

    @Bean
    public SimpleAsyncTaskExecutor taskExecutor() {
        SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
        executor.setConcurrencyLimit(5);
        return executor;
    }

    @Bean
    public MyTask myTask() {
        return new MyTask();
    }

    public class MyTask {
        public void runTask() {
            System.out.println("Task is running - " + System.currentTimeMillis());
        }
    }

    public void scheduleTask() {
        TaskScheduler scheduler = taskScheduler();
        SimpleAsyncTaskExecutor executor = taskExecutor();
        MyTask task = myTask();
        scheduler.schedule(() -> executor.execute(task::runTask), new CronTrigger("0/5 * * * * ?"));
    }
}

上記のサンプル コードでは、まず TaskScheduler と TaskExecutor を定義します。これらは、それぞれタスク スケジューラとタスク エグゼキュータをカスタマイズするために使用されます。このうち、TaskScheduler はスレッド プール サイズやスレッド名のプレフィックスなどのパラメーターを構成できる ThreadPoolTask​​Scheduler 実装を使用し、TaskExecutor は同時実行制限などのパラメーターを構成できる SimpleAsyncTaskExecutor 実装を使用します。

次に、特定のタスク ロジックを実行するための runTask() メソッドを含む MyTask クラスを定義します。次に、@Bean アノテーションを使用して MyTask クラスを Spring Bean として登録します。

最後に、タスクのスケジューリングを開始するために、scheduleTask() メソッドを定義します。このメソッドは、TaskScheduler と CronTrigger を使用してタスク スケジュールのトリガー モードを定義し、TaskExecutor を使用してタスクを実行します。この例では、5 秒ごとにタスク スケジュールをトリガーする Cron 式を定義します。タスクのスケジュール設定がトリガーされると、MyTask の runTask() メソッドが呼び出され、特定のタスク ロジックが実行されます。

カスタム タスク スケジューラとタスク エグゼキュータを有効にするには、MyTaskConfig クラスを Spring Bean として登録し、アプリケーションの起動時に @PostConstruct アノテーションを介してscheduleTask() メソッドを呼び出してタスク スケジューリングを開始する必要があることに注意してください。

エピローグ

この記事では、Java での並行プログラミングとタスクのスケジューリングにスレッドを使用する方法について説明します。スレッドを使用するとプログラムの同時処理能力が向上し、タスクのスケジューリングを使用するとプログラムの自動処理能力が向上します。この記事では、読者の役に立つことを願って、JDK のネイティブ スレッドとタスク スケジューリング関数、および一般的に使用されるサードパーティ クラス ライブラリである Quartz と Spring Task を紹介します。

スレッドとタスクのスケジュールを使用するプロセスでは、スレッド セーフやタスクの繰り返しなどの問題に注意する必要があります。特にマルチスレッド環境では、スレッド セーフを確保するためにロックを追加する必要があります。同時に、メモリリークやリソースの消費などの問題にも注意し、システムクラッシュを避けるためにプログラムの無限ループや大量のスレッドの作成を避けるように努める必要もあります。

スレッドとタスクのスケジューリングは Java の非常に重要な機能であり、コア テクノロジと最適化スキルを習得し、プログラムのパフォーマンスと信頼性を向上させるには、慎重な学習と実践が必要です。

おすすめ

転載: blog.csdn.net/bairo007/article/details/132446401