MoreExecutors工具類使用


MoreExecutors是guava提供的工具類,是對jdk自帶的Executors工具類的擴展,主要方法如下:

1、addDelayedShutDown()方法的兩個重載:

public static void addDelayedShutDownHook(ExecutorService service, Duration terminationTimeout)

public static void addDelayedShutDownHook(ExecutorService service, long terminationTimeout, TimeUnit unit)

給線程池增加一個關閉鈎子,在線程池中的線程是守護線程(daemon thread)時有用,用戶線程(user thread)執行完后,jvm不會立即關閉,而是等待一定時間。正常情況下,只要用戶線程一結束,jvm就會立即關閉,而不管守護線程任務是否執行完畢。從這里也可以看出,盡量不要把自定義線程搞成守護線程,不然是作死。

示例:

    public static void main(String[] args) {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("async-pool-%d").build();
        ExecutorService executorService = new ThreadPoolExecutor(10, 20, 0, TimeUnit.MINUTES, new LinkedBlockingQueue<>(3000), threadFactory);
        executorService.submit(() -> {
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "@666");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(Thread.currentThread().getName() + "@888");
        MoreExecutors.addDelayedShutdownHook(executorService, 3000, TimeUnit.MILLISECONDS);
    }

上例中,如果沒有調用MoreExecutors.addDelayedShutdownHook()方法的話,只會打印888,不會打印666。因為打印888的線程是用戶線程,打印666的線程是守護線程(setDaemon(true)),用戶線程一執行完,jvm就關閉了,所以不會有2000ms之后的666打印。假如創建ThreadFactory實例時,沒有調用setDaemon(true),即創建的線程是非守護線程,那么會先打印main@888,2000ms后會打印async-pool-0@666,因為main線程和async-pool-0線程都是用戶線程。調用MoreExecutors.addDelayedShutdownHook()方法后,jvm會在用戶線程結束后等待一段時間再關閉,這段之間守護線程可以工作,到了時間,jvm關閉,守護線程也完蛋了,事情干多少是多少,沒干完就沒干完。

2、getExitingExecutorService()方法的三個重載:把一個ThreadPoolExecutor實例轉成一個應用結束后自動退出的ExecutorService實例。

public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor)

public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor, Duration terminationTimeout)

public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit)

示例:

    public static void main(String[] args) {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("async-pool-%d").build();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 0, TimeUnit.MINUTES, new LinkedBlockingQueue<>(3000), threadFactory);
        ExecutorService executorService = MoreExecutors.getExitingExecutorService(threadPoolExecutor);
        executorService.submit(() -> {
            try {
                Thread.sleep(110000);
                System.out.println(Thread.currentThread().getName() + "@666");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(Thread.currentThread().getName() + "@888");
    }

在用戶線程執行完后,jvm會至多等待一定時間(默認是120s),以期線程池線程執行結束。這里不要求線程池中線程是守護線程,因為不管是不是守護線程,getExitingExecutorService()方法都會把這個線程池包裝成守護線程的線程池,並且加個默認120s的關閉鈎子。 

3、getExitingScheduledExecutorService()方法的三個重載:道理同getExitingExecutorService()方法差不多

public static ScheduledExecutorService getExitingScheduledExecutorService(ScheduledThreadPoolExecutor executor)

public static ScheduledExecutorService getExitingScheduledExecutorService(ScheduledThreadPoolExecutor executor, Duration terminationTimeout)

public static ScheduledExecutorService getExitingScheduledExecutorService(ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit)

4、listeningDecorator()方法的兩個重載:

public static ListeningExecutorService listeningDecorator(ExecutorService delegate):把一個ExecutorService實例轉成一個ListeningExecutorService實例

示例:

    public static void main(String[] args) {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("async-pool-%d").build();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 0, TimeUnit.MINUTES, new LinkedBlockingQueue<>(3000), threadFactory);
        ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(threadPoolExecutor);
        ListenableFuture future = listeningExecutorService.submit(() -> {
            try {
                Thread.sleep(4000);
                System.out.println(Thread.currentThread().getName() + "@666");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 1);
        Futures.addCallback(future, new FutureCallback() {

            @Override
            public void onSuccess(Object result) {
                System.out.println(Thread.currentThread().getName() + "@" + result);
            }

            @Override
            public void onFailure(Throwable t) {
                System.out.println(Thread.currentThread().getName() + "@" + t.getMessage());
            }
        }, threadPoolExecutor);
        System.out.println(Thread.currentThread().getName() + "@888");
    }

ListeningExecutorService實例的submit()方法返回值是一個ListenableFuture實例,利用Futures工具類可以給這個實例添加callback回調。

public static ListeningScheduledExecutorService listeningDecorator(ScheduledExecutorService delegate):把一個ScheduledExecutorService實例轉成一個ListeningScheduledExecutorService實例

5、directExector()方法:direct是直接的意思。

public static Executor directExecutor():返回一個Executor實例,具體是DirectExecutor類型,DirectExecutor是一個實現了Executor接口的枚舉類。調用execute(Runnable command)方法時,在當前線程執行任務,而不會另起一個線程。這個方法沒卵用,在當前線程執行的話,直接寫代碼就好了,干嘛還整這個。

6、newDirectExecutorService()方法

public static ListeningExecutorService newDirectExecutorService():返回一個ListeningExecutorService實例,具體是DirectExecutorService類型,DirectExecutorService是MoreExecutors的內部類,繼承了AbstractListeningExecutorService。和DirectExecutor實例類似,調用DirectExecutorService實例的submit()方法時,會在當前線程執行任務,而不會另起一個線程。同樣,沒卵用。這里還要吐槽一下DirectExecutorService類名的不規范,媽的,明明是ListeningExecutorService實現類,就不能叫DirectListeningExecutorService嗎。

7、newSequentialExecutor()方法:

public static Executor newSequentialExecutor(Executor delegate):把一個Executor實例包裝成一個順序執行的Executor實例,具體是SequentialExecutor類型。線程池按照任務添加順序執行任務。

示例:

    public static void main(String[] args) {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("async-pool-%d").build();
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(10, 20, 0, TimeUnit.MINUTES, new LinkedBlockingQueue<>(3000), threadFactory);
        Executor executor = MoreExecutors.newSequentialExecutor(threadPoolExecutor);
        for (int i = 0; i < 10; i++) {
            int d = i;
            executor.execute(() -> {
                try {
                    Thread.sleep(d * 1000);
                    System.out.println(
                            Thread.currentThread().getName() + "@" + d + ", now= " + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

線程池按照任務添加順序執行任務。上一個任務執行不完,下一個任務就不會開始。活活把一個線程池包裝成了一個單線程的線程池,雞肋。

8、platformThreadFactory()方法:

MoreExecutors.platformThreadFactory():返回默認的ThreadFactory實例。內部調用Executors.defaultThreadFactory(),返回DefaultThreadFactory實例。DefaultThreadFactory是Executors的內部類,實現了ThreadFactory接口。DefaultThreadFactory對於的線程名形如pool-0-thread-0,pool-0-thread-1,我們自定義線程池時線程的名字絕對不可能這么沒有意義,所以platformThreadFactory()這個方法也是雞肋。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM