Java並發基礎總結,java並發基礎
並發是一種能並行運行多個程式或並行運行一個程式中多個部分的能力。如果程式中一個耗時的任務能以非同步或並行的方式運行,那麼整個程式的輸送量和可 互動性將大大改善。現代的PC都有多個CPU或一個CPU中有多個核,是否能合理運用多核的能力將成為一個大規模應用程式的關鍵。
線程基本使用
編寫線程運行時執行的代碼有兩種方式:一種是建立Thread子類的一個執行個體並重寫run方法,第二種是建立類的時候實現Runnable介面。當然,實現 Callable也算是一種方式,Callable和Future結合實現可以實現在執行完任務後擷取傳回值,而Runnable和Thread方式是無法擷取任務執行後的結果的。
public class ThreadMain { public static void main(String[] args) { MyThread myThread = new MyThread(); new Thread(myThread).start(); new MyThreas2().start(); }}// 第一種方式,實現Runable介面class MyThread implements Runnable { @Override public void run() { System.out.println("MyThread run..."); }}// 第二種方式,繼承Thread類,重寫run()方法class MyThreas2 extends Thread { @Override public void run() { System.out.println("MyThread2 run..."); }}
一旦線程啟動後start()方法會立即返回,而不會等待run()方法執行完畢後返回,就好像run方法是在另外一個cpu上執行一樣。
注意:建立並運行一個線程所犯的常見錯誤是調用線程的run()方法而非start()方法,如下所示:
Thread newThread = new Thread(MyRunnable());newThread.run(); //should be start();
起初你並不會感覺到有什麼不妥,因為run()方法的確如你所願的被調用了。但是,事實上,run()方法並非是由剛建立的新線程所執行的,而是當前線程所執行了。也就是被執行上面兩行代碼的線程所執行的。想要讓建立的新線程執行run()方法,必須調用新線程的start方法。
Callable和Future結合實現實現在執行完任務後擷取傳回值:
public static void main(String[] args) { ExecutorService exec = Executors.newSingleThreadExecutor(); Future<String> future = exec.submit(new CallTask()); System.out.println(future.get());}class CallTask implements Callable { public String call() { return "hello"; }}
給線程設定線程名:
MyTask myTask = new MyTask();Thread thread = new Thread(myTask, "myTask thread");thread.start();System.out.println(thread.getName());
當建立一個線程的時候,可以給線程起一個名字。它有助於我們區分不同的線程。
volatile
在多線程並發編程中synchronized和Volatile都扮演著重要的角色,Volatile是輕量級的synchronized,它在多處理器開發中保證了共用變數的“可見度”。可見度的意思是當一個線程修改一個共用變數時,另外一個線程能讀到這個修改的值。它在某些情況下比synchronized的開銷更小,但是volatile不能保證變數的原子性。
volatile變數進行寫操作時(彙編下有lock指令),該lock指令在多核系統下有2個作用:
- 將當前CPU緩衝行寫回系統記憶體。
- 這個寫回操作會引起其他CPU緩衝了改地址的資料失效。
多CPU下遵循緩衝一致性原則,每個CPU通過嗅探在匯流排上傳播的資料來檢查自己的緩衝值是否到期了,當發現緩衝對應的記憶體位址被修改,將對應緩衝行設定為無效狀態,下次對資料操作會從系統記憶體重新讀取。更多volatile知識請點擊深入分析Volatile的實現原理。
synchronized
在多線程並發編程中Synchronized一直是元老級角色,很多人都會稱呼它為重量級鎖,但是隨著Java SE1.6對Synchronized進行了各種最佳化之後,有些情況下它並不那麼重了。
Java中每一個對象都可以作為鎖,當一個線程試圖訪問同步代碼塊時,它首先必須得到鎖,退出或拋出異常時必須釋放鎖。
- 對於同步方法,鎖是當前執行個體對象。
- 對於靜態同步方法,鎖是當前對象的Class對象。
- 對於同步方法塊,鎖是Synchonized括弧裡配置的對象。
synchronized關鍵字是不能繼承的,也就是說基類中的synchronized方法在子類中預設並不是synchronized的。當線程試圖訪問同步代碼塊時,必須先獲得鎖,退出或拋出異常時釋放鎖。Java中每個對象都可以作為鎖,那麼鎖存在哪裡呢?鎖存在Java對象頭中,如果對象是數群組類型,則虛擬機器用3個word(字寬) 儲存物件頭,如果對象是非數群組類型,則用2字寬儲存物件頭。更多synchronized知識請點擊Java SE1.6中的Synchronized。
線程池
線程池負責管理背景工作執行緒,包含一個等待執行的任務隊列。線程池的任務隊列是一個Runnable集合,背景工作執行緒負責從任務隊列中取出並執行Runnable對象。
ExecutorService executor = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) { executor.execute(new MyThread2());}executor.shutdown();
Java通過Executors提供了4種線程池:
- newCachedThreadPool:建立一個可緩衝線程池,對於新任務如果沒有空閑線程就新建立一個線程,如果空閑線程超過一定時間就會回收。
- newFixedThreadPool:建立一個固定數量線程的線程池。
- newSingleThreadExecutor:建立一個單線程的線程池,該線程池只用一個線程來執行任務,保證所有任務都按照FIFO順序執行。
- newScheduledThreadPool:建立一個定長線程池,支援定時及週期性任務執行。
以上幾種線程池底層都是調用ThreadPoolExecutor來建立線程池的。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
- corePoolSize(線程池的基本大小):當提交一個任務到線程池時,線程池會建立一個線程來執行任務,即使其他閒置基本線程能夠執行新任務也會建立線程,等到需要執行的任務數大於線程池基本大小時就不再建立。如果調用了線程池的prestartAllCoreThreads方法,線程池會提前建立並啟動所有基本線程。
- maximumPoolSize(線程池最大大小):線程池允許建立的最大線程數。如果隊列滿了,並且已建立的線程數小於最大線程數,則線程池會再建立新的線程執行任務。值得注意的是如果使用了無界的任務隊列這個參數就沒什麼效果。
- keepAliveTime(線程活動保持時間):線程池的背景工作執行緒空閑後,保持存活的時間。所以如果任務很多,並且每個任務執行的時間比較短,可以調大這個時間,提高線程的利用率。
- TimeUnit(線程活動保持時間的單位):可選的單位有天(DAYS),小時(HOURS),分鐘(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。,可以選擇的阻塞隊列有以下幾種:
- workQueue(任務隊列):用於儲存等待執行的任務的阻塞隊列。
-
- ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。
- LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,輸送量通常要高於ArrayBlockingQueue。靜態Factory 方法Executors.newFixedThreadPool()使用了這個隊列。
- SynchronousQueue:一個不儲存元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,輸送量通常要高於LinkedBlockingQueue,靜態Factory 方法Executors.newCachedThreadPool使用了這個隊列。
- PriorityBlockingQueue:一個具有優先順序得無限阻塞隊列。
當提交新任務到線程池時,其處理流程如下:
參考:
1、深入分析Volatile的實現原理
2、Java SE1.6中的Synchronized