簡體   English   中英

如何在 Java 中無限期等待?

[英]How to wait indefinitely in Java?

我有一個依賴於Timer和調度的應用程序,而它的 main 方法在執行應用程序設置后什么都不做。

我看到這個成語用了很多:

public static void main(final String[] args) {
    // schedule all tasks
    while (true) {
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (final InterruptedException ignored) {
            Thread.interrupted();
        }
    }
}

有沒有更好的方法來寫這個? 出於某種原因,我真的需要這個while循環嗎? (似乎與Thread.interrupted()有關)

tl;博士

不要自己管理線程。

使用Executors框架創建一對執行器服務。

  • 一個執行器服務可以執行任何請求的任務。
  • 另一個調度的執行器服務可以無限期地反復檢查傳入的請求,在檢查之間休息指定的時間以釋放 CPU 內核。 當找到一個請求時,這個執行器服務會安排一個RunnableCallable在另一個執行器服務上執行。

執行者框架

有沒有更好的表達方式?

是的,有更好的方法。

使用執行器框架。 請參閱 Oracle 的教程

發明了這個框架,因此我們不必直接管理線程。 大多數情況都可以由 Executors 框架處理,只有極少數情況需要程序員來處理線程。

Executors 框架取代了TimerTimerTask類。 Timer class Javadoc 中所述:

Java 5.0 引入了 java.util.concurrent package 並且其中一個並發實用程序是以給定速率重復執行任務的 ScheduledThreadPool 或並發實用程序。 它實際上是 Timer/TimerTask 組合的更通用替代品,因為它允許多個服務線程,接受各種時間單位,並且不需要子類化 TimerTask(只需實現 Runnable)。 使用一個線程配置 ScheduledThreadPoolExecutor 使其等效於 Timer。

我假設您嘗試做的是偶爾發現需要完成的任務,然后在后台線程上執行該任務。

為此,我們需要兩個執行器服務,每個服務都由一個線程池支持。

  • 我們需要執行器服務來執行我們發現隨着時間的推移需要完成的任務。 為此,我們通過Executors.newCachedThreadPool使用無界線程池。 我們稱之為我們的workers執行服務。 如果您有 CPU 密集型任務,您可能需要由配置為最大線程數的線程池支持的替代執行器服務。
  • 我們需要一個預定的執行器服務,它可以被告知每隔一段時間,無限期地重復一個或多個任務。 我們在這里要分配一個任務,該任務查找傳入請求的內容,以便在其他執行程序服務上安排其他任務。 您沒有在問題中解釋,但我想您正在檢查隊列、數據庫、email 或文件的存在,以獲取要運行的新作業列表。 檢查工作是您在此計划的執行程序服務上作為重復任務所做的工作。 這個執行器服務在它的池中只需要一個線程,並且因為沒有更好的名字而被命名為dispatcher器。

這是模擬代碼的一個版本,我們碰巧發現每次檢查時只需要完成一個任務。 我們使用一個隨機數來模擬一些被請求的任意任務。 為了保持代碼整潔,我們使用了 Java 14 中的新開關表達式功能。開關中的每個案例都會生成一個可運行的Runnable ,並將該可運行的 ZA8CFDE6331BD59EB26666F89111C4 提交給執行器服務。 執行器服務在提交時立即執行每個可運行的run方法。 生成匿名Runnable object 的代碼部分是這個 lambda 語法:

( ) -> System.out.println( "Running Fruit report. Now: " + Instant.now() )

您也可以使用常規語法來生成Runnable 您可能希望定義單獨的類來定義您放置任務代碼的Runnable

public class FruitReport implements Runnable
{
    public void run() {
        System.out.println( "Running Fruit report. Now: " + Instant.now() ) ;
    }
}

完整的示例代碼。

package work.basil.example;

import java.time.Instant;
import java.util.concurrent.*;

public class TimerTaskManager
{
    ExecutorService workers;
    ScheduledExecutorService dispatcher;


    private void launch ( )
    {
        System.out.println( "INFO - Method `launch` running at " + Instant.now() );
        this.workers = Executors.newCachedThreadPool();
        this.dispatcher = Executors.newSingleThreadScheduledExecutor();
        this.dispatcher.scheduleWithFixedDelay(
                ( ) -> {
                    // Check for whatever input you have that prompts for tasks to be performed.
                    // We use a random number generator to simulate arbitrary work requests arriving.
                    // Using the new switch expressions feature in Java 14. https://openjdk.java.net/jeps/361
                    int r = ThreadLocalRandom.current().nextInt( 1 , 6 ); // inclusive-to-exclusive.
                    switch ( r )
                    {
                        case 1 -> this.workers.submit( ( ) -> System.out.println( "Running Fruit report. Now: " + Instant.now() ) );  // Passing an anonymous `Runnable` object to the `ExecutorService::submit` method. 
                        case 2 -> this.workers.submit( ( ) -> System.out.println( "Running Wine report. Now: " + Instant.now() ) );
                        case 3 -> this.workers.submit( ( ) -> System.out.println( "Running Clothing report. Now: " + Instant.now() ) );
                        case 4 -> this.workers.submit( ( ) -> System.out.println( "Running Appliance report. Now: " + Instant.now() ) );
                        case 5 -> this.workers.submit( ( ) -> System.out.println( "Running Tools report. Now: " + Instant.now() ) );
                        default -> System.out.println( "ERROR - Unexpected r value: " + r );
                    }
                } ,
                3 ,
                10 ,
                TimeUnit.SECONDS
        );
    }

    private void shutdown ( )
    {
        this.dispatcher.shutdown();
        this.workers.shutdown();
        System.out.println( "INFO - Method `shutdown` running at " + Instant.now() );
    }

    public static void main ( String[] args )
    {
        TimerTaskManager app = new TimerTaskManager();
        app.launch();

        try
        {
            Thread.sleep( TimeUnit.MINUTES.toMillis( 1 ) );
        }
        catch ( InterruptedException e )
        {
            e.printStackTrace();
        }
        finally
        {
            app.shutdown();
        }
    }
}

跑的時候。

INFO - Method `launch` running at 2020-06-28T04:16:17.742443Z
Running Wine report. Now: 2020-06-28T04:16:20.786653Z
Running Tools report. Now: 2020-06-28T04:16:30.787891Z
Running Appliance report. Now: 2020-06-28T04:16:40.791585Z
Running Wine report. Now: 2020-06-28T04:16:50.796355Z
Running Fruit report. Now: 2020-06-28T04:17:00.800407Z
Running Appliance report. Now: 2020-06-28T04:17:10.805166Z
INFO - Method `shutdown` running at 2020-06-28T04:17:17.783938Z

讓我們稍微復雜一點,以更好地模擬您可能的實際情況,即每次您計划的執行程序服務檢查時都會發現一些任意數量的請求處於待處理狀態。 為了模擬這一點,我們隨機重復 switch 語句。

package work.basil.example;

import java.time.Instant;
import java.util.concurrent.*;

public class TimerTaskManager
{
    ExecutorService workers;
    ScheduledExecutorService dispatcher;


    private void launch ( )
    {
        System.out.println( "INFO - Method `launch` running at " + Instant.now() );
        this.workers = Executors.newCachedThreadPool();
        this.dispatcher = Executors.newSingleThreadScheduledExecutor();
        this.dispatcher.scheduleWithFixedDelay(
                ( ) -> {
                    // Check for whatever input you have that prompts for tasks to be performed.
                    int countRequests = ThreadLocalRandom.current().nextInt( 1 , 7 );
                    System.out.println( "INFO - Found " + countRequests + " incoming requests for work to be done. Now: " + Instant.now() );
                    for ( int i = 1 ; i <= countRequests ; i++ )
                    {
                        // We use a random number generator to simulate arbitrary work requests arriving.
                        // Using the new switch expressions feature in Java 14. https://openjdk.java.net/jeps/361
                        int r = ThreadLocalRandom.current().nextInt( 1 , 6 ); // inclusive-to-exclusive.
                        switch ( r )
                        {
                            case 1 -> this.workers.submit( ( ) -> System.out.println( "Running Fruit report. Now: " + Instant.now() ) );  // Passing an anonymous `Runnable` object to the `ExecutorService::submit` method. 
                            case 2 -> this.workers.submit( ( ) -> System.out.println( "Running Wine report. Now: " + Instant.now() ) );
                            case 3 -> this.workers.submit( ( ) -> System.out.println( "Running Clothing report. Now: " + Instant.now() ) );
                            case 4 -> this.workers.submit( ( ) -> System.out.println( "Running Appliance report. Now: " + Instant.now() ) );
                            case 5 -> this.workers.submit( ( ) -> System.out.println( "Running Tools report. Now: " + Instant.now() ) );
                            default -> System.out.println( "ERROR - Unexpected r value: " + r );
                        }
                    }
                } ,
                3 ,
                10 ,
                TimeUnit.SECONDS
        );
    }

    private void shutdown ( )
    {
        this.dispatcher.shutdown();
        this.workers.shutdown();
        System.out.println( "INFO - Method `shutdown` running at " + Instant.now() );
    }

    public static void main ( String[] args )
    {
        TimerTaskManager app = new TimerTaskManager();
        app.launch();

        try
        {
            Thread.sleep( TimeUnit.MINUTES.toMillis( 1 ) );
        }
        catch ( InterruptedException e )
        {
            e.printStackTrace();
        }
        finally
        {
            app.shutdown();
        }
    }
}

跑的時候。

INFO - Method `launch` running at 2020-06-28T04:34:52.097616Z
INFO - Found 2 incoming requests for work to be done. Now: 2020-06-28T04:34:55.112823Z
Running Tools report. Now: 2020-06-28T04:34:55.122258Z
Running Appliance report. Now: 2020-06-28T04:34:55.122653Z
INFO - Found 2 incoming requests for work to be done. Now: 2020-06-28T04:35:05.127456Z
Running Appliance report. Now: 2020-06-28T04:35:05.128309Z
Running Clothing report. Now: 2020-06-28T04:35:05.128297Z
INFO - Found 5 incoming requests for work to be done. Now: 2020-06-28T04:35:15.128481Z
Running Tools report. Now: 2020-06-28T04:35:15.129414Z
Running Wine report. Now: 2020-06-28T04:35:15.129430Z
Running Appliance report. Now: 2020-06-28T04:35:15.129663Z
Running Tools report. Now: 2020-06-28T04:35:15.130001Z
Running Fruit report. Now: 2020-06-28T04:35:15.130441Z
INFO - Found 4 incoming requests for work to be done. Now: 2020-06-28T04:35:25.133727Z
Running Clothing report. Now: 2020-06-28T04:35:25.133880Z
Running Wine report. Now: 2020-06-28T04:35:25.133917Z
Running Wine report. Now: 2020-06-28T04:35:25.133967Z
Running Wine report. Now: 2020-06-28T04:35:25.134148Z
INFO - Found 6 incoming requests for work to be done. Now: 2020-06-28T04:35:35.136503Z
Running Tools report. Now: 2020-06-28T04:35:35.136663Z
Running Wine report. Now: 2020-06-28T04:35:35.136733Z
Running Clothing report. Now: 2020-06-28T04:35:35.136764Z
Running Clothing report. Now: 2020-06-28T04:35:35.136735Z
Running Appliance report. Now: 2020-06-28T04:35:35.137363Z
Running Clothing report. Now: 2020-06-28T04:35:35.137349Z
INFO - Found 3 incoming requests for work to be done. Now: 2020-06-28T04:35:45.136728Z
Running Appliance report. Now: 2020-06-28T04:35:45.136943Z
Running Clothing report. Now: 2020-06-28T04:35:45.136940Z
Running Tools report. Now: 2020-06-28T04:35:45.136948Z
INFO - Method `shutdown` running at 2020-06-28T04:35:52.111007Z

注意:確保在不再需要或退出應用程序時正常關閉每個執行程序服務。 否則,它們的后備線程池可能會像僵屍一樣無限期地繼續存在。

注意:將提交給計划執行程序服務的代碼包裝在 try-catch 中,以防出現任何意外異常和可能的錯誤。 如果一個throwable冒泡到達預定的執行器服務,該服務會默默地停止安排更多的工作。 搜索堆棧溢出以了解更多信息。

Thread.currentThread().join();

將休眠直到 JVM 被殺死。

有關說明,請參閱Java 中的 Thread.currentThread().join() 的使用。

暫無
暫無

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

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