執行一個異步任務你還只是以下new Thread嗎?html
new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start();
那你就太out了,new Thread的弊端以下:java
相比new Thread,Java提供的四種線程池的好處在於:數據庫
Java經過Executors提供四種線程池,分別爲:
Method submit extends base method Executor.execute by creating and returning a Future that can be used to cancel execution and/or wait for completion.
用到返回值的例子,好比說我有不少個作validation的task,我但願全部的task執行完,而後每一個task告訴我它的執行結果,是成功仍是失敗,若是是失敗,緣由是什麼.
There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute this exception will Go to the uncaught exception handler (when you don’t have provided one explicitly, the default one will just print the stack trace to System.err). If you submitted the task with submit any thrown exception, checked or not, is then part of the task’s return status. For a task that was submitted with submit and that terminates with an exception, the Future.get will rethrow this exception, wrapped in an ExecutionException.
意思就是若是你在你的task裏會拋出checked或者unchecked exception,而你又但願外面的調用者可以感知這些exception並作出及時的處理,那麼就須要用到submit,經過捕獲Future.get拋出的異常.
import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ExecutorServiceTest { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); List<Future<String>> resultList = new ArrayList<Future<String>>(); // 建立10個任務並執行 for (int i = 0; i < 10; i++) { // 使用ExecutorService執行Callable類型的任務,並將結果保存在future變量中 Future<String> future = executorService.submit(new TaskWithResult(i)); // 將任務執行結果存儲到List中 resultList.add(future); } executorService.shutdown(); // 遍歷任務的結果 for (Future<String> fs : resultList) { try { System.out.println(fs.get()); // 打印各個線程(任務)執行的結果 } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { executorService.shutdownNow(); e.printStackTrace(); return; } } } } class TaskWithResult implements Callable<String> { private int id; public TaskWithResult(int id) { this.id = id; } /** * 任務的具體過程,一旦任務傳給ExecutorService的submit方法,則該方法自動在一個線程上執行. * * @return * @throws Exception */ public String call() throws Exception { System.out.println("call()方法被自動調用,幹活!!! " + Thread.currentThread().getName()); if (new Random().nextBoolean()) throw new TaskException("Meet error in task." + Thread.currentThread().getName()); // 一個模擬耗時的操做 for (int i = 999999999; i > 0; i--) ; return "call()方法被自動調用,任務的結果是:" + id + " " + Thread.currentThread().getName(); } } class TaskException extends Exception { public TaskException(String message) { super(message); } }
執行的結果相似於:編程
call()方法被自動調用,幹活!!! pool-1-thread-1 call()方法被自動調用,幹活!!! pool-1-thread-2 call()方法被自動調用,幹活!!! pool-1-thread-3 call()方法被自動調用,幹活!!! pool-1-thread-5 call()方法被自動調用,幹活!!! pool-1-thread-7 call()方法被自動調用,幹活!!! pool-1-thread-4 call()方法被自動調用,幹活!!! pool-1-thread-6 call()方法被自動調用,幹活!!! pool-1-thread-7 call()方法被自動調用,幹活!!! pool-1-thread-5 call()方法被自動調用,幹活!!! pool-1-thread-8 call()方法被自動調用,任務的結果是:0 pool-1-thread-1 call()方法被自動調用,任務的結果是:1 pool-1-thread-2 java.util.concurrent.ExecutionException: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3 at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at com.cicc.pts.ExecutorServiceTest.main(ExecutorServiceTest.java:29) Caused by: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3 at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:57) at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619)
能夠看見一旦某個task出錯,其它的task就中止執行.緩存
能夠關閉 ExecutorService,這將致使其拒絕新任務.提供兩個方法來關閉 ExecutorService.
shutdown() 方法在終止前容許執行之前提交的任務,
shutdownNow() 方法阻止等待任務啓動並試圖中止當前正在執行的任務.在終止時執行程序沒有任務在執行,也沒有任務在等待執行,而且沒法提交新任務.關閉未使用的 ExecutorService 以容許回收其資源.
通常分兩個階段關閉 ExecutorService.第一階段調用 shutdown 拒絕傳入任務,而後調用 shutdownNow(若有必要)取消全部遺留的任務安全
// 啓動一次順序關閉,執行之前提交的任務,但不接受新任務.多線程
threadPool.shutdown();
若是是一個多線程協做程序,好比菲波拉切數列,1,1,2,3,5,8…使用多線程來計算.
但後者須要前者的結果,就須要用callable接口了.
callable用法和runnable同樣,只不過調用的是call方法,該方法有一個泛型返回值類型,你能夠任意指定.
runnable接口實現的沒有返回值的併發編程.
callable實現的存在返回值的併發編程.(call的返回值String受泛型的影響) 使用Future獲取返回值.
這裏寫圖片描述併發
建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程.示例代碼以下:app
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(index); } }); }
線程池爲無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的線程,而不用每次新建線程.dom
建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待.示例代碼以下:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); }
由於線程池大小爲3,每一個任務輸出index後sleep 2秒,因此每兩秒打印3個數字.
定長線程池的大小最好根據系統資源進行設置.如Runtime.getRuntime().availableProcessors().可參考PreloadDataCache.
建立一個定長線程池,支持定時及週期性任務執行.延遲執行示例代碼以下:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); scheduledThreadPool.schedule(new Runnable() { @Override public void run() { System.out.println("delay 3 seconds"); } }, 3, TimeUnit.SECONDS);
表示延遲3秒執行.
按期執行示例代碼以下:
scheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("delay 1 seconds, and excute every 3 seconds"); } }, 1, 3, TimeUnit.SECONDS);
表示延遲1秒後每3秒執行一次.
ScheduledExecutorService比Timer更安全,功能更強大,後面會有一篇單獨進行對比.
建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行.示例代碼以下:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; singleThreadExecutor.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); }
結果依次輸出,至關於順序執行各個任務.
現行大多數GUI程序都是單線程的.Android中單線程可用於數據庫操做,文件操做,應用批量安裝,應用批量刪除等不適合併發但可能IO阻塞性及影響UI線程響應的操做.
總結:
SimpleDateFormat來作Date到String的類型轉換,建議使用Apache commons-lang中的FastDateFormat。
由於JDK裏自帶的SimpleDateFormat存在線程不安全問題。
maven依賴:
<dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.5</version> </dependency>
代碼:
private String initDate() { Date d = new Date(); FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss"); return fdf.format(d); }
多線程問題,主要是多線程執行時的順序是隨機的,沒法保證同一代碼的執行順序,任意兩步代碼(非原子)操做都存在安全問題
多線程問題,主要是多線程執行時的順序是隨機的,沒法保證同一代碼的執行順序,任意兩步代碼(非原子)操做都存在安全問題
String str="a"; synchronized(str){ str = "b"; //str變爲一個新對象,鎖失效,字符的賦值是新new一個String而後賦值的 }
i++,使用java.util.concurrent.atomic下的原子類代替來作多線程的計數器
i++是兩步,讀取i的變量的值,而後更新+1,因此不安全, 使用AtomicInteger
HashMap,ArrayList,使用ConcurrentHashMap,CopyOnWriteArrayList代替
StringBuilder,使用StringBuffer代替
參考引用:
https://www.cnblogs.com/Steve...
https://blog.csdn.net/insistg...
https://blog.csdn.net/c511362...