java 后台接口另起一個線程執行其他業務


直接post代碼,然后在詳細介紹為什么:

package com.xxx.testset.service.impl;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 
 * @function xxx接口實現類
 * @author Liangjw
 * @date 2019-9-18 下午02:09:33
 * @version    
 * @since JDK 1.7
 */
public class TestSetServiceImpl extends BizServiceImpl implements TestSetService {    
   
    ExecutorService executorService = Executors.newSingleThreadExecutor();
  
   ...
@Override
public String startTest(String testId) { final String test_Id = testId; Connection con = DBConnectUtil.getConnection(); PreparedStatement pstmt = null,pstmt2 = null; ResultSet resultSet = null; try { Date nowDate = new Date(); final String nowDateStr = DateUtil.dateTransformStr(nowDate, DATE_FORMAT); Map<String, Object> map = new HashMap<String, Object>(); map.put("TEST_ID", testId); //獲取檢測裝置集---2020年7月10日16:40:44 ljw final List<DataRecord> deviceList = this.getDao().queryForDataSet(STATEMENT_KEY + "selectPdDetectionDevice", map).getResults(); map.put("TEST_STATE", "檢測中"); map.put("BEGIN_TIME", nowDateStr); //獲取當前機具表中第一條數據,補充到檢測任務中---2020/07/02 ljw Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put("TYPE_NAME", "檢測車"); List<DataRecord> mechanicsList = this.getDao().queryForDataSet(STATEMENT_KEY + "selectMechanics", parameters).getResults();
       ...
//啟動一個單線程(其實是創建線程池,只不過是單線程化的線程池),單獨調用算法分析檢測結果 executorService.execute(new Runnable() { @Override public void run() { //實現Runnable接口中的run方法,此處寫入自己想要執行的內容! Boolean test_flag = true;//判斷任務檢測狀態,“檢測中”為true,“已完成”為false Connection con = DBConnectUtil.getConnection(); PreparedStatement pstmt = null; ResultSet resultSet = null; String cmdIds = ""; for (int x = 0; x < deviceList.size(); x++) { cmdIds+=deviceList.get(x).getString("CMD_ID") + ","; } cmdIds = cmdIds.substring(0, cmdIds.length() - 1); String sql = "select qp.GUID, qp.PhaseDataA, qp.PDDataA, qp.PhaseDataB, qp.PDDataB, qp.PhaseDataC, qp.PDDataC, tf.TimeFieldDataA, tf.TimeFieldDataB, tf.TimeFieldDataC, qp.ReceiveTime , qp.DeviceID " + "from tb_QPhiData qp left join tb_TimeFieldData tf on qp.GUID = tf.GUID_QPhi where qp.DeviceID in ("+cmdIds+") and qp.ReceiveTime > ? order by qp.ReceiveTime"; int flag = 0;//標識調用算法次數 String startTime = nowDateStr;//開始檢測時間 String lastTime = null;//上次查詢結果的最新時間
            ...
if(!test_flag) { executorService.shutdown(); //關閉線程 } } // run()方法執行代碼部分,到此為止! }); //涉及線程代碼部分,到此為止! } catch (Exception e) { e.printStackTrace(); return "檢測失敗"; }finally { try { if(resultSet != null){ resultSet.close(); } if(pstmt != null){ pstmt.close(); } if(pstmt2 != null){ pstmt2.close(); } if(con != null){ con.close(); } } catch (SQLException e) { e.printStackTrace(); } } return "檢測成功"; } }

我這里使用了ExecutorService創建線程池,線程執行時調用execute(Runnable),關閉線程時調用ExecutorService.shutdown()方法。

一、ExecutorService介紹

ExecutorService是Java中對線程池定義的一個接口,它在java.util.concurrent包中,在這個接口中定義了和后台任務執行相關的方法:

 

 

 Java API對ExecutorService接口的實現有兩個,所以這兩個即是Java線程池具體實現類(詳細了解這兩個實現類,點擊這里):

1. ThreadPoolExecutor
2. ScheduledThreadPoolExecutor

除此之外,ExecutorService還繼承了Executor接口(注意區分Executor接口和Executors工廠類),這個接口只有一個execute()方法,最后我們看一下整個繼承樹:

 

 

 

二、ExecutorService的創建

創建一個什么樣的ExecutorService的實例(即線程池)需要根據具體應用場景而定,不過Java給我們提供了一個Executors工廠類,它可以幫助我們很方便的創建各種類型ExecutorService線程池,Executors一共可以創建下面這四類線程池:

1. newCachedThreadPool 創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
2. newFixedThreadPool 創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
3. newScheduledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。
4. newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

備注:Executors只是一個工廠類,它所有的方法返回的都是ThreadPoolExecutorScheduledThreadPoolExecutor這兩個類的實例。

三、ExecutorService的使用

ExecutorService executorService = Executors.newFixedThreadPool(10);

executorService.execute(new Runnable() {
public void run() {
    System.out.println("Asynchronous task");
}
});

executorService.shutdown();

四、ExecutorService的執行

ExecutorService有如下幾個執行方法:

- execute(Runnable)
- submit(Runnable)
- submit(Callable)
- invokeAny(...)
- invokeAll(...)

4.1 execute(Runnable)

這個方法接收一個Runnable實例,並且異步的執行,請看下面的實例:

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.execute(new Runnable() {
public void run() {
    System.out.println("Asynchronous task");
}
});

executorService.shutdown();

這個方法有個問題,就是沒有辦法獲知task的執行結果。如果我們想獲得task的執行結果,我們可以傳入一個Callable的實例(下面會介紹)。

4.2 submit(Runnable)

submit(Runnable)execute(Runnable)區別是前者可以返回一個Future對象,通過返回的Future對象,我們可以檢查提交的任務是否執行完畢,請看下面執行的例子:

ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Runnable() {
public void run() {
    System.out.println("Asynchronous task");
}
});

future.get();  //returns null if the task has finished correctly.

如果任務執行完成,future.get()方法會返回一個null。注意,future.get()方法會產生阻塞。

4.3 submit(Callable)

submit(Callable)submit(Runnable)類似,也會返回一個Future對象,但是除此之外,submit(Callable)接收的是一個Callable的實現,Callable接口中的call()方法有一個返回值,可以返回任務的執行結果,而Runnable接口中的run()方法是void的,沒有返回值。請看下面實例:

ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
    System.out.println("Asynchronous Callable");
    return "Callable Result";
}
});

System.out.println("future.get() = " + future.get());

如果任務執行完成,future.get()方法會返回Callable任務的執行結果。注意,future.get()方法會產生阻塞。

4.4 invokeAny(…)

invokeAny(...)方法接收的是一個Callable的集合,執行這個方法不會返回Future,但是會返回所有Callable任務中其中一個任務的執行結果。這個方法也無法保證返回的是哪個任務的執行結果,反正是其中的某一個。請看下面實例:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 2";
}
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
    return "Task 3";
}
});

String result = executorService.invokeAny(callables);
System.out.println("result = " + result);
executorService.shutdown();

大家可以嘗試執行上面代碼,每次執行都會返回一個結果,並且返回的結果是變化的,可能會返回“Task2”也可是“Task1”或者其它。

4.5 invokeAll(…)

invokeAll(...)與 invokeAny(...)類似也是接收一個Callable集合,但是前者執行之后會返回一個Future的List,其中對應着每個Callable任務執行后的Future對象。情況下面這個實例:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 1";
}
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
    return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 3";
}
});

List<Future<String>> futures = executorService.invokeAll(callables);

for(Future<String> future : futures){
System.out.println("future.get = " + future.get());
}

executorService.shutdown();

五、ExecutorService的關閉

當我們使用完成ExecutorService之后應該關閉它,否則它里面的線程會一直處於運行狀態。

舉個例子,如果的應用程序是通過main()方法啟動的,在這個main()退出之后,如果應用程序中的ExecutorService沒有關閉,這個應用將一直運行。之所以會出現這種情況,是因為ExecutorService中運行的線程會阻止JVM關閉。

如果要關閉ExecutorService中執行的線程,我們可以調用ExecutorService.shutdown()方法。在調用shutdown()方法之后,ExecutorService不會立即關閉,但是它不再接收新的任務,直到當前所有線程執行完成才會關閉,所有在shutdown()執行之前提交的任務都會被執行。

如果我們想立即關閉ExecutorService,我們可以調用ExecutorService.shutdownNow()方法。這個動作將跳過所有正在執行的任務和被提交還沒有執行的任務。但是它並不對正在執行的任務做任何保證,有可能它們都會停止,也有可能執行完成。

注:關於Java線程池類ThreadPoolExecutor、ScheduledThreadPoolExecutor及Executors工廠類更詳細介紹,點擊這里

 

參考來源,點擊這里


免責聲明!

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



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