1. 程式人生 > >Java多執行緒超時範圍內等待完成的幾種方法

Java多執行緒超時範圍內等待完成的幾種方法

        在工程專案中可能會有這麼一個場景,客戶端處理層需要從服務端(CDN/圖片伺服器)獲取n張圖片(參考微博一個人最多有9張圖片),那麼問題來了,如何在一定的時間範圍內儘可能多的獲取到圖片。當然,最為簡單粗暴的方法就是通過序列的方式來獲取,但是如果第一個請求hang住並超時,那麼其他圖片都無法獲取,這顯然不是一個好的設計方式。這個問題設計到兩點,第一點是"儘可能多",相比於序列,解決方案就是多執行緒並行;第二點是"一定時間範圍內",也就是超時時間,因此這個問題就可以抽象為多執行緒在超時範圍內獲取資料的方法,下面探討幾張方法。

一、thread.join(long millis) (不滿足需求)

       在JavaDoc中join(time)功能為線上程退出之前將會最多等待time毫秒。事實上,如果超時時間過了,那麼主執行緒將會繼續停止阻塞(可見join方法是阻塞的)並繼續執行,這麼做有一個弊端可能會造成所謂的"執行緒洩露"。此外,我們通常會呼叫interrupt來中斷執行緒,當然這麼做也是不提倡的。

TimeoutDemo.java

public class TimeoutDemo {

    public static void main(String[] args) {

        try {

            int threadCount = 3;

            int defaultTimes = 10;

            Thread[] ts = new Thread[threadCount];

            for (int i=0; i < threadCount; i++) {
                ts[i] = new Thread(new TimeoutRunnable(defaultTimes));
            }

            for (int i=0; i < threadCount; i++) {
                ts[i].start();
            }

            for (int i=0; i < threadCount; i++) {
                ts[i].join(1000);
                System.out.println("i = " + i + " has joined...");
            }

            for (int i=0; i < threadCount; i++) {
                ts[i].interrupt();
            }

        } catch (Exception e) {
            System.out.println("thread interrupted when waiting join.");
        } finally {
            System.out.println("all threads finished...");
        }
    }
}

class TimeoutRunnable implements Runnable{

    private int times;

    public TimeoutRunnable(int times) {
        this.times = times;
    }

    @Override
    public void run() {
        for (int i = 0; i < times; i++) {
            System.out.println("thread id: " + Thread.currentThread().getId() + " runs for " + (i+1) + "th time.");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("thread id: " + Thread.currentThread().getId() + " is interrupted while running and stop right now.");

                return;
            }
        }

        System.out.println("thread id: " + Thread.currentThread().getId() + " finished.");
    }
}

執行結果

thread id: 11 runs for 1th time.
thread id: 13 runs for 1th time.
thread id: 12 runs for 1th time.
i = 0 has joined...
thread id: 12 runs for 2th time.
thread id: 11 runs for 2th time.
thread id: 13 runs for 2th time.
i = 1 has joined...
thread id: 11 runs for 3th time.
thread id: 12 runs for 3th time.
thread id: 13 runs for 3th time.
i = 2 has joined...
thread id: 13 runs for 4th time.
thread id: 11 runs for 4th time.
thread id: 12 runs for 4th time.
thread id: 11 is interrupted while running and stop right now.
thread id: 13 is interrupted while running and stop right now.
all threads finished...
thread id: 12 is interrupted while running and stop right now.

可見,join方法是阻塞的,依次等待執行緒超時,就本例來講,等待了3個1000ms,不滿足在一定的timeout內完成。

二、基於ExecutorService的shutdown() 和 awaitTermination(long timeout, TimeUnit unit)方法:

        awaitTermination是一個阻塞方法,在ExecutorService執行完shutdown之後進行操作,awaitTermination將會阻塞,直到所有任務完成了執行,或者超時產生,或者執行緒中斷髮生為止。如果所有任務完成則返回true;如果在任務完成之前發生了超時則返回false。程式碼示例,TimeoutDemo1.java

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TimeoutDemo1 {

    public static void main(String[] args) {

        try {

            int threadCount = 10;

            int defaultTimes = 10;

            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);

            for (int i=0; i < threadCount * 2; i++) {
                executorService.submit(new TimeoutRunnable1(defaultTimes));
            }

            executorService.shutdown();

            if(!executorService.awaitTermination(7, TimeUnit.SECONDS)) {
                System.out.println("executors did not terminate in the specified time.");
                List<Runnable> droppedTasks = executorService.shutdownNow();
                System.out.println("executors was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed.");
            }
        } catch (Exception e) {
            System.out.println("exception: " + e.getMessage());
        } finally {
            System.out.println("scheduled executor service closed normally.");
        }
    }
}

class TimeoutRunnable1 implements Runnable{

    private int times;

    public TimeoutRunnable1(int times) {
        this.times = times;
    }

    @Override
    public void run() {
        for (int i = 0; i < times; i++) {
            System.out.println("thread id: " + Thread.currentThread().getId() + " runs for " + (i+1) + "th time.");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("thread id: " + Thread.currentThread().getId() + " is interrupted while running and stop right now.");

                return;
            }
        }
        System.out.println("thread id: " + Thread.currentThread().getId() + " finished.");
    }
}

關於shutdown和shutdonwNow的區別參考前一篇文章:如何優雅的關閉執行緒池

三、使用CountDownLatch中的await(long timeout, TimeUnit unit)

    如果在timeout時間範圍內,count變成了0,那麼await返回true;如果超過了超時時間,count不為0,那麼返回false。

看程式碼TimeoutDemo2.java

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TimeoutDemo2 {

    public static void main(String[] args) {

        try {

            int threadCount = 10;

            int defaultTimes = 10;

            CountDownLatch countDownLatch = new CountDownLatch(threadCount * 2);

            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);

            for (int i=0; i < threadCount * 2; i++) {
                executorService.submit(new TimeoutRunnable2(defaultTimes, countDownLatch));
            }

            if(!countDownLatch.await(19, TimeUnit.SECONDS)) {
                executorService.shutdown();
                System.out.println("executors did not terminate in the specified time.");
                List<Runnable> droppedTasks = executorService.shutdownNow();
                System.out.println("executors was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed.");
            }
        } catch (Exception e) {
            System.out.println("exception: " + e.getMessage());
        } finally {
            System.out.println("scheduled executor service closed normally.");
        }
    }
}

class TimeoutRunnable2 implements Runnable{

    private int times;

    private CountDownLatch countDownLatch;

    public TimeoutRunnable2(int times,  CountDownLatch countDownLatch) {
        this.times = times;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < times; i++) {
                System.out.println("thread id: " + Thread.currentThread().getId() + " runs for " + (i + 1) + "th time.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("thread id: " + Thread.currentThread().getId() + " is interrupted while running and stop right now.");

                    return;
                }
            }

            System.out.println("thread id: " + Thread.currentThread().getId() + " finished.");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            countDownLatch.countDown();
        }
    }
}

四、Future.get(long timeout, TimeUnit unit)(不完全滿足需求,不最優)

      future.get在一定的超時時間範圍內獲取執行緒執行的返回值,但是get方法是阻塞的,會在一定的超時時間之內阻塞,直到任務完成返回,或者這時間超時丟擲TimeoutException,程式碼如下:

TimeoutDemo3.java:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class TimeoutDemo3 {

    public static void main(String[] args) {

        try {
            int threadCount = 10;

            int defaultTimes = 10;

            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);

            List<Future> futureList = new ArrayList<Future>();

            for (int i=0; i < threadCount; i++) {
                Future future = executorService.submit(new TimeoutRunnable3(defaultTimes));
                futureList.add(future);
            }

            for (Future future: futureList) {
                String str = (String)future.get(15, TimeUnit.SECONDS);

                System.out.println(str);
            }
        } catch (InterruptedException e) {
            System.out.println("future is interrupted...");
        } catch (ExecutionException e) {
            System.out.println("future execution exception...");
        } catch (TimeoutException e) {
            System.out.println("future timeout exception...");
        } finally {
            System.out.println("scheduled executor service closed normally.");
        }
    }
}

class TimeoutRunnable3 implements Callable<String>{

    private int times;

    public TimeoutRunnable3(int times) {
        this.times = times;
    }

    @Override
    public String call() {
        try {
            for (int i = 0; i < times; i++) {
                System.out.println("thread id: " + Thread.currentThread().getId() + " runs for " + (i + 1) + "th time.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("thread id: " + Thread.currentThread().getId() + " is interrupted while running and stop right now.");
                }
            }

            //System.out.println("thread id: " + Thread.currentThread().getId() + " finished.");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        return "thread id: " + Thread.currentThread().getId() + " finished";
    }
}
這麼做的問題在於,一旦最前面的執行緒阻塞了,超時了並丟擲異常,那麼其他完成的執行緒都無法拿到結果。

Author:憶之獨秀

Email:[email protected]

相關推薦

Java執行超時範圍等待完成方法

        在工程專案中可能會有這麼一個場景,客戶端處理層需要從服務端(CDN/圖片伺服器)獲取n張圖片(參考微博一個人最多有9張圖片),那麼問題來了,如何在一定的時間範圍內儘可能多的獲取到圖片。當然,最為簡單粗暴的方法就是通過序列的方式來獲取,但是如果第一個請求hang

Java執行-----實現生產者消費者模式的方式

   1 生產者消費者模式概述      生產者消費者模式就是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞佇列來進行通訊,所以生產者生產完資料之後不用等待消費者處理, 直接扔給阻塞佇列,消費者不找生產者要資料,

Java執行生產者與消費者等待喚醒機制(示例)

在下面新建的兩條執行緒,兩條執行緒操作的物件都是學生類,一條執行緒生產學生物件的資料,一條執行緒消費學生物件的資料,且做到,有資料才消費,沒資料就等待,沒資料就生產,有資料就等待。 第一個案例是學生類物件,非常的簡單就定義了兩個成員變數,以及一個用於喚醒執行緒的標記。 成員變數預設會賦值

java 執行 之匿名部類實現執行

package 匿名內部類實現多執行緒; public class Demo { public static void main(String[] args) { // 繼承Thread類實現多執行緒 new Thread() {

java 執行 資料流 部類(播放音樂)

一.多執行緒 1.程序與執行緒 程序:負責資源管理(記憶體) 執行緒:負責的程式的執行 2.類實現 a、定義一個類,繼承Thread b、重寫Run方法 c、啟動執行緒(執行緒物件.start()

Java執行生產者消費者說明等待喚醒機制問題和虛假喚醒問題

不用等待喚醒機制實現的生產者與消費者 程式碼 package com.hust.juc; /* * 生產者和消費者案例 */ public class TestProductorAndConsumer { public static vo

java執行、FutureTask的用法及兩常用的使用場景

Java多執行緒實現的方式有四種 1.繼承Thread類,重寫run方法 2.實現Runnable介面,重寫run方法,實現Runnable介面的實現類的例項物件作為Thread建構函式的target 3.通過Callable和FutureTask建立執行緒 4.通過執行緒池

java:執行(實現Runnable的原理)及二方式的區別

* 1,看Thread類的建構函式,傳遞了Runnable介面的引用  * 2,通過init()方法找到傳遞的target給成員變數的target賦值 * 3,檢視run方法,發現run方法中有判斷,如果target不為null就會呼叫Runnable介面子類物件的run方法 *

Java執行中注入Spring的Bean-使用靜態方法直接取的容器中的spring物件

目前認為比較好的解決方案。 1,工具類 public class SpringApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext context

Java執行併發總結】Thread類的常用方法(join、yield等)---執行的基礎操作篇

 啟動(start)   最基本的操作,呼叫Runnable中的run方法,無返回值。 new Thread(new Test()).start(); 休眠(sleep)  使當前執行緒休眠一段時間,預設為毫秒級,最高可以精確到納秒,呼叫的方法為slee

Java執行 鎖優化】鎖的三狀態切換

引言 在多執行緒併發程式設計中Synchronized一直是元老級角色,很多人都會稱呼它為重量級鎖,但是隨著Java SE1.6對Synchronized進行了各種優化之後,有些情況下它並不那麼重了,本文詳細介紹了Java SE1.6中為了減少獲得鎖和

Java執行中,Join和Interrupt()方法的使用

更多詳細的解答請轉至:http://my.oschina.net/summerpxy/blog/198457;http://uule.iteye.com/blog/1101994;(比如有一個執行緒t.當在Main執行緒中呼叫t.join()的時候,那麼Main執行緒必須拿

Java執行之死鎖的出現和解決方法

什麼是死鎖?死鎖是這樣一種情形:多個執行緒同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放.由於執行緒被無限期地阻塞,因此程式不能正常執行.形象的說就是:一個寶藏需要兩把鑰匙來開啟,同時間正好來了兩個人,他們一人一把鑰匙,但是雙方都再等著對方能交出鑰匙來開啟寶藏,誰都沒

使用部類來將執行隱藏在類中的方法

class InnerThread1{ private int countDown=5; private Inner inner; private class Inner extends Thread{ Inner(String na

執行同步和互斥有哪實現方法

執行緒間的同步方法大體可分為兩類:使用者模式和核心模式。顧名思義,核心模式就是指利用系統核心物件的單一性來進行同步,使用時需要切換核心態與使用者態,而使用者模式就是不需要切換到核心態,只在使用者態完成操作。使用者模式下的方法有:原子操作(例如一個單一的全域性變數),臨界區。核

linux執行程式設計,替代sleep的方式

我只想要程序的某個執行緒休眠一段時間的,可是用sleep()是將整個程序都休眠的,這個可能就達不到,我們想要的效果了。 目前我知道有三種方式: 1 usleep    這個是輕量級的, 聽說能可一實現執行緒休眠, 我個人並不喜歡這種方式,所以我沒有驗證它的可行信(個人不推薦

執行下解決資源競爭的7方法

前言   一般情況下,只要涉及到多執行緒程式設計,程式的複雜性就會顯著上升,效能顯著下降,BUG出現的概率大大提升。 多執行緒程式設計本意是將一段程式並行執行,提升資料處理能力,但是由於大部分情況下都涉及到共有資源的競爭,所以修改資源 物件時必須加鎖處理。但是鎖的實現有很多種方法,下面就來一起了解一下在

Shell中生成一定範圍隨機整數方法

在Shell指令碼中可以用random,隨機裝置(/dev/random,/dev/urandom),date,uuid,甚至md5sum等命令來生成隨機數字或字母。如果要生成某一範圍內的隨機整數的情

執行掛起和恢復的方法

執行緒掛起和恢復方法(1):sleep()方法 此種方法比較簡單,哪個執行緒需要掛起就在哪個執行緒中直接呼叫:Thread.sleep(掛起的毫秒數); 執行緒掛起和恢復方式(2):join()方法 在實現多執行緒的兩種方法這篇博文中值說明了Threa

(三) Java執行詳解之執行範圍共享變數及ThreadLocal類使用

執行緒範圍內共享變數 HashTable方式實現 在開發中經常會遇到一種情況:有一個變數會被多個執行緒訪問,但是要確保同個執行緒內訪問的是同一個物件,Hashtable方式實現程式碼如下: public class ThreadExample5 {