本章內容: * 什麼是線程 * 中斷線程 * 線程狀態 * 線程屬性 * 同步 * 阻塞隊列 * 線程安全的集合 * Collable與Future * 執行器 * 同步器 * 線程與Swing
t.setDaemon(true)
將線程轉換爲守護線程( daemon thread )。守護線程的惟一用途是爲其餘線程提供服務。計時線程就是一個例子,它定時地發送「計時器嘀嗒」信號給其餘線程或清空過期的高速緩存項的線程。當只剩下守護線程時,虛擬機就退出了,因爲若是隻剩下守護線程,就不必繼續運行程序了。void uncaughtException(Thread t,Throwable e)能夠用 setUncaughtExceptionHandler 方法爲任何線程安裝一個處理器。也能夠用 Thread 類的靜態方法 setDefaultUncaughtExceptionHandler 爲全部線程安裝一個默認的處理器。替換處理器可使用日誌API發送未捕獲異常的報告到日誌文件。
myLock.lock(); //a ReentrantLock object try { critical section } finally { myLock.unlock();//make sure the lock is unlocked even if an exception is three }這一結構確保任什麼時候刻只有一個線程進入臨界區。一旦一個線程封鎖了鎖對象,其餘任何線程都沒法經過 lock 語句。當其餘線程調用 lock 時,它們被阻塞,直到第一個線程釋放鎖對象。
while(!(ok to proceed)) condition.await();
intrinsicCondition.await(); intrinsicCondition.signalAll();
synchronized(obj) //this is the synchronized block { critical section }因而它得到 obj 的鎖。
private volatile boolean done; public void flipDone(){done = !done;} //not atomic不能確保翻轉域中的值。
final Map<String,Double> accounts = new HashMap();其餘線程會在構造函數完成構造以後纔看到這個 accounts 變量。
public static final ThreadLocal< SimpleDateFormat > dateFormat = new ThreadLocal< SimpleDateFomrat >() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }要訪問具體的格式化方法,能夠調用:
String dateStamp = dateFormat.get().format(new Date());在一個給定線程中首次調用 get 時,會調用 initilaValue 方法。在此以後, get 方法會返回屬於當前線程的那個實例。
int random = ThreadLocalRandom.current().nextInt(upperBound);ThreadLocalRandom.current() 調用會返回特定於當前線程的 Random 類實例。
myCondition.await(100,TimeUnit.MILLISECONDS)若是一個線程被另外一個線程經過調用 signalAll 或 signal 激活,或者超時時限已達到,或者線程被中斷,那麼 await 方法將返回。
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();(2)抽取讀鎖和寫鎖:
private Lock readLock = rwl.readLock(); private Lock writeLock = rwl.writeLock();(3)對全部的獲取方法加讀鎖:
public double getTotalBalance() { readLock.lock(); try{...} finally{readLock.unlock();} }(4)對全部的修改方法加寫鎖:
public void transfer(...) { writeLock.lock(); try{...} finally{writeLock.unlock();} }
方法 | 正常動做 | 特殊狀況下的動做 |
---|---|---|
add | 添加一個元素 | 若是隊列滿,則拋出IllegalStateException異常 |
element | 返回隊列的頭元素 | 若是隊列空,拋出NoSuchElementException異常 |
offer | 添加一個元素並返回true | 若是隊列滿,返回false |
peek | 返回隊列的頭元素 | 若是隊列空,則返回null |
poll | 移出並返回隊列的頭元素 | 若是隊列空,則返回null |
put | 添加一個元素 | 若是隊列滿,則阻塞 |
remove | 移出並返回頭元素 | 若是隊列空,則拋出NoSuchElementException異常 |
take | 移出並返回頭元素 | 若是隊列空,則阻塞 |
boolean success = q.offer(x,100,TimeUnit.MILLISECONDS);嘗試在100毫秒的時間內在隊列的尾部插入一個元素。若是成功返回true;不然,達到超時時,返回false。相似地,下面的調用:
Object head = q.poll(100,TimeUnit.MILLISEDS);嘗試用100毫秒的時間移除隊列的頭元素;若是成功返回頭元素,不然,達到在超時時,返回null。
interface Delayed extends Comparable< Delayed > { long getDelay(TimeUnit unit); }getDelay方法返回對象的殘留延遲。負值表示延遲已經結束。元素只有在延遲用完的狀況下才能從DelayQueue移除。還必須實現compareTo方法。DelayQueue使用該方法對元素進行排序。
q.transfer(item);這個調用會阻塞,直到另外一個線程將元素(item)刪除。LinkedTransferQueue實現了這個接口。
cache.putIfAbsent(key,value);相反的操做是刪除(或許應該叫作removeIfPresent)。調用
cache.remove(key,value);將原子性地刪除鍵值對,若是它們在映像表中出現的話。最後,
cache.replace(key,oldValue,newValue);原子性地用新值替換舊值,假定舊值與指定的鍵值關聯。
List< E > synchArrayList = Collections.synchronizedList(new ArrayList< E >()); Map< K,V > synchHashMap = Collections.synchronizedMap(new HashMap< K,V >());
synchronized(synchHashMap) { Iterator< K > iter = synchHashMap.keySet().iterator(); while(iter.hashNext())...; }
public interface Callable< V > { V call() throws Exception; }類型參數是返回值的類型。例如,Callable< Integer >表示一個最終返回Integer對象的異步計算。
public interface Future< V > { V get() thros ...; V get(long timeout,TimeUnit unit) throwa...; void cancel(boolean mayInterupt); boolean isCancelled(); boolean isDone(); }第一個get方法的調用被阻塞,直到計算完成。若是在計算完成以前,第二個方法的調用超時,拋出一個TimeoutException異常。若是運行該計算的線程被中斷,兩個方法都將拋出InterruptedException。若是計算已經完成,那麼get方法當即返回。
方法 | 描述 |
---|---|
newCachedThreadPool | 必要時建立新線程;空閒線程會被保留60秒 |
newFixedThreadPool | 該池包含固定數量的線程;空閒線程會一直被保留 |
newSingleThreadExecutor | 只有一個線程的「池」,該線程順序執行每個提交的任務(相似於Swing事件分配線程) |
newScheduledThreadPool | 用於預約執行而構建的固定線程池,替代java.util.Timer |
newSingleThreadScheduleExecutor | 用於預約執行而構建的單線程「池」 |
Future<?> submit(Runnable task) Future< T > submit(Runnable task,T result) Future< T > submit(Callable< T > task)該池會在方便的時候儘早執行提交的任務。調用submit時,會獲得一個Future對象,可用來查詢該任務的狀態。
List<Callable< T >> tasks=...; List<Future< T >> results = executor.invokeAll(tasks); for (Future< T > result:results) processFurther(result.get());這個方法的缺點是若是第一個任務恰巧花去了不少時間,則可能按可得到的順序保存起來更有實際意義。能夠用ExecutorCompletionService來進行排列。
ExecutorCompletionService service = new ExecutorCompletionService(executor); for(Callable< T > task:tasks) service.submit(task); for (int i=0;i<task.size();i++) processFurther(service.task().get());
if(problemSize > threshold) solve problem direckly else { break problem into subproblems recursively solve each subproblem combine the results }
類 | 它能作什麼 | 什麼時候使用 |
---|---|---|
CyclicBarrier | 容許線程集等待直至其中預約數目的線程到達一個公共障柵(barrier),而後能夠選擇執行一個處理障柵的動做 | 當大量的線程須要在它們的結果可用以前完成時 |
CountDownLatch | 容許線程集等待直到計數器減爲0 | 當一個或多個線程須要等待直到指定數目的事件發生 |
Exchanger | 容許兩個線程在要交換的對象準備好時交換對象 | 當兩個線程工做在同一個數據結構的兩個實例上的時候,一個向實例添加數據而另外一個從實例清除數據 |
Semaphore | 容許線程集等待直到被容許繼續運行爲止 | 限制訪問資源的線程總數。若是許可數是1,經常阻塞線程直到另外一個線程給出許可爲止 |
SynchronoutQueue | 容許一個線程把對象交給另外一個線程 | 在沒有顯式同步的狀況下,當兩個線程準備好將一個對象從一個線程傳遞到另外一個時 |
將線程與 Swing 一塊兒使用時,必須遵循兩個簡單的原則。java
制定第一條規則的理由易於理解。若是花不少時間在事件分配線程上,應用程序像「死了」同樣,由於它不響應任何事件。特別是,事件分配線程應該永遠不要進行 input/output 調用,這有可能會阻塞,而且永遠不要調用 sleep 。(若是須要等待指定的時間,使用定時器事件。)
第二條規則在 Swing 編程中一般稱爲單一線程規則( single-thread rule )。
這兩條規則看起來彼此衝突。假定要啓動一個獨立的線程運行一個耗時的任務。線程工做的時候,一般要燈芯用戶界面中指示執行的進度。任務完成的時候,要再一次更新 GUI 界面。可是,不能從本身的線程接觸 Swing 組件。例如,若是要更新進度條或標籤文本,不能從線程中設置它的值。
要解決這一問題,在任何線程中,可使用兩種有效的方法向事件隊列添加任意的動做。例如,假定想在一個線程中週期性地更新標籤來代表進度。不能夠從本身的線程中調用 label.setText ,而應該使用 EventQueue 類的 invokeLater 方法和 invokeAndWait 方法使所調用的方法在事件分配線程中執行。
應該將 Swing 代碼放置到實現 Runnable 接口的類的 run 方法中。而後,建立該類的一個對象,將其傳遞給靜態的 invokeLater 或 invokeAndWait 方法。
當事件放入事件隊列時, invokeLater 方法當即返回,而 run 方法被異步執行。 invokeAndWait 方法等待直到 run 方法確實被執行過爲止。
有更新進度標籤時, invokeLater 方法更適宜。用戶更但願讓工做器線程有更快完成工做而不是獲得更加精確的進度指示器。
這兩種方法都是在事件分配線程中執行 run 方法。沒有新的線程被建立。程序員