java多線程和線程池

1.創建線程

方法有兩種:繼承Thread實現run()方法,實現runnable實run()方法,其實thread類也是實現了runnable接口的,
TestThread t=new TestThread();
啓動線程t.start();

同一個線程對象只能啓動一次,調用多次start()是無效的,出現異常。

實現Runnable接口相對於繼承Thread類來說,有如下顯著的優勢:
(1)、適合多個相同程序代碼的線程去處理同一資源的情況,把虛擬CPU(線程)同程序的代碼、數據有效分離,較好地體現了面向對象的設計思想。

(2)、可以避免由於Java的單繼承特性帶來的侷限。開發中經常碰到這樣一種情況,即:當要將已經繼承了某一個類的子類放入多線程中,由於一個類不能同時有兩個父類,所以不能用繼承Thread類的方式,那麼就只能採用實現Runnable接口的方式了。

(3)、增強了程序的健壯性,代碼能夠被多個線程共享,代碼與數據是獨立的。當多個線程的執行代碼來自同一個類的實例時,即稱它們共享相同的代碼。多個線程可以操作相同的數據,與它們的代碼無關。當共享訪問相同的對象時,即共享相同的數據。當線程被構造時,需要的代碼和數據通過一個對象作爲構造函數實參傳遞進去,這個對象就是一個實現了Runnable接口的類的實例。

2 線程的狀態和生命週期

1.七種狀態

i.剛創建,尚未啓動

ii.Runnable 可執行,start(),yield()

iii.Blocked 受阻塞的線程,notify(),notifyall,synchronized(),join()

iv.Waiting 等待線程無期限wait()

v.Timed_waiting的有期限等待,sleep()

vi.Running正在執行的線程

vii.Terminated 已退出,interrupt()

這裏寫圖片描述

線程的幾個重要的方法:

1.sleep():很常見的,讓當前線程讓出cpu時間片,同時進入睡眠狀態,該方法不會釋放對象的機鎖,如果在synchroized()代碼塊或者方法內,其他線程依然是無法訪問被同步的對象。

2.wait(),當前線程進入被同步對象的等待池內,同時釋放對象的機鎖,其他線程可以訪問被鎖對象,可以指定睡眠時間或者調用notify()或者notifyAll()喚醒在等待池的線程。

wait(),notify()和notifyAll()必須在synchronized 塊內調用,否則出現異常。

3.join(),在當前線程使用t.join(),即使當前線程在t線程執行完之後繼續執行。

4.yield(),當前線程讓出時間片,和其他線程一起競爭下一次執行時間。

5.Interrupt(),線程被阻塞時,用t.interrupt(),向線程拋出一個異常,讓其捕獲從而提早將線程阻塞狀態結束

6.終止線程,線程終止後不能再次start(),會出現異常

2.1線程的屬性

1.Set,線程名稱,優先級,是否爲daemon

2.Get,線程id,名稱,優先級,狀態,線程池,是否爲活動,是否daemon,是否中斷

3.currentThread()獲取當前線程

3. daemon線程和用戶線程

線程優先級和調度
1.1,5,10三個等級,一般爲5
2.優先級高,只是執行的概率高,不具有先後嚴格的順序
3.setPriority,設置優先級

1.只有當所有的用戶線程終止,進程纔算終止。

2.Deamon線程是服務線程,優先級低,如服務器偵聽,jvm垃圾回收,是個無限循環,所有用戶線程都結束了,deamon自動結束

3.進程中只有後臺線程運行時,進程就會結束。

4.線程池和線程組

線程組

1.線程組管理線程,設置優先級,等屬性,安全控制。

2.線程組必須從屬於其他線程組,默認是系統主線程組。

3.將線程加入到線程組需要先創建線程組對象,將其作爲線程構造函數參數。

4.List()輸出線程樹,enumerate()複製線程組中所有線程到一個線程數組中

線程組:線程組存在的意義,首要原因是安全。java默認創建的線程都是屬於系統線程組,而同一個線程組的線程是可以相互修改對方的數據的。但如果在不同的線程組中,那麼就不能“跨線程組”修改數據,可以從一定程度上保證數據安全。

線程池

線程池:線程池存在的意義,首要作用是效率。線程的創建和結束都需要耗費一定的系統時間(特別是創建),不停創建和刪除線程會浪費大量的時間。所以,在創建出一條線程並使其在執行完任務後不結束,而是使其進入休眠狀態,在需要用時再喚醒,那麼 就可以節省一定的時間。如果這樣的線程比較多,那麼就可以使用線程池來進行管理。保證效率。

==一般情況下,線程越多佔用內存也越大,併發量也越大。==

線程組和線程池共有的特點:
1,都是管理一定數量的線程

2,都可以對線程進行控制—包括休眠,喚醒,結束,創建,中斷(暫停)但並不一定包含全部這些操作。

線程池的相關類和方法

1.Callable

其功能和Runable類似,但是個泛型接口,有一個返回值的call()方法,

2.Future

future 是指定了線程管理規範的接口,具有取消,查詢是否完成,獲取執行結果,設置結果等操作,

3.Futuretask

futureTask 是future的實現類,同時也實現了Runnable接口,所以具備了管理線程的能力,還包裝了Callable接口,其實Runnable最終也會被傳爲Callable接口,FutureTask執行的任務類型都是Callable,

FutureTask即可以有Thread來執行也可以提交給ExecuteService執行,

ExcutorService接口

線程池都實現了該接口包括submit()execute(),shutdown()等方法,線程池的聲明週期包括:運行,關閉,終止。創建後便進入運行狀態,調用shutdown()進入關閉狀態,此時不在接受 新任務,但一進提及哦啊的任務繼續運行。當所有已提交任務執行完進入終止狀態。

一個實現類ThreadPoolExecutor的構造方法:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

解釋一下BlockingQueue,阻塞隊列即隊列滿時加入阻塞,有鏈式和數組實現的,優先級的,還有SynchronousQueue

其他參數解釋參見另一文章。== ==

常用的線程池有四類:fixedThreadPool、ScheduledThreadPool、cachedThreadPool、singleThreadPool

Android的asyncTask默認使用的是singleThreadPool線程池

線程同步

線程安全:多個線程同時調用同一個對象的方法時,對另一線程產生負面影響,如果一個類不受這個影響則是線程安全類。

爲了保證數據的一致性,同步保證同一時刻只有一個線程能訪問數據,對其修改後將修改後的值更新到內存,是其他線程能獲得新數據。

1.synchronized同步

java對象自帶了對象鎖屬性,可以對其進行多線程的訪問同步,獲取指定對象(可以爲object)的互斥鎖,就可以進行鎖定

1).同步方法synchronized void f(),鎖的是該方法所在類的對象

2).synchronized 同步代碼塊(臨界區),鎖指定對象,如果是Class對象,則鎖定整個類,

1.Synchronized同步時會產生死鎖

2.當一個線程獲得一個對象的同步鎖,而等待另一個資源時可以用wait()將當前線程阻塞,釋放其同步鎖,然後調用notify()通知其他正在等待該對象鎖的線程,然後另一個線程獲得同步鎖後提供前一個線程的資源,調用notify()通知其他等待線程

3.Notify()喚醒一個等待的咸亨,隨機一個,notifyall()全部喚醒

4.生產消費者模型

a)同一個容器的get,和put方法,都進行同步

b)一個生產者,一個消費者線程使用同一個容器,但條件不滿足時wait(),執行完後notify()通知其他的等待的線程

c)必須在類P中定義一個新的成員變量bFull來表示數據存儲空間的狀態,當Consumer線程取走數據後,bFull值爲false,當Producer線程放入數據後,bFull值爲true。只有bFull爲true時,Consumer線程才能取走數據,否則就必須等待Producer線程放入新的數據後的通知;反之,只有bFull爲false,Producer線程才能放入新的數據,否則就必須等待Consumer線程取走數據後的通知。

volatile 同步

是一種弱同步,保證每次更新都會更新到內存。當一個變量定義成volatile之後, 保證了此變量對所有線程的可見性,也就是說當一條線程修改了這個變量的值,新的值對於其它線程來說是可以立即得知的.此時,該變量的讀寫操作直接在主內存中完成.

==Volatile 變量具有 synchronized 的可見性特性,但是不具備原子特性。==

原子性:即一個線程對變量操作時,另一個線程不能對該變量進行操作。

Volatile variables share the visibility features of synchronized, but none of the atomicity features.

雖然增量操作(x++)看上去類似一個單獨操作,實際上它是一個由==讀取-修改-寫入==操作序列組成的組合操作,必須以原子方式執行,而 volatile 不能提供必須的原子特性。

可重入鎖–ReentrantLock和condition

和synchronized比較:

1.獲取和釋放的靈活性

2.輪訓鎖和定時鎖

3.公平鎖。

reentrantlock的相關方法:

lock() 獲取鎖;

tryLock(),tryLock(long timeout,TimeUnit )嘗試獲取鎖,超時嘗試獲取鎖;

unlock()解鎖

newCondition()獲取鎖 的condition,作用類似object的wait()和notify()

lock和unlock必須成對出現,否則可能發生死鎖,在finally代碼塊中unlock(),而Synchronized代碼塊不需要手動釋放鎖。因爲ReentrantLock只是普通的java類。而synchronized則包括了鎖定的信息。

信號量,Semaphore

信號量本質是ReetrantLock,信號量維護了一個信號量許可集的個數,規定了最大允許多少線程同時訪問數據,通過acquire()獲得許可,一個線程獲得許可,信號量的可用許可減一,通過release()釋放許可,釋放之後可用許可加1。

如果acquire沒有可用許可,那麼線程會阻塞。

構造方法:Semaphore(int size)

例如:食堂的5個窗口,有10個人打飯菜,那麼最大隻有5人同時進行打飯菜。

循環柵欄 CyclicBarrier

是一個同步輔助類,允許一組線程等待,釋放等待線程之後可重用。
CyclicBarrier(int size,Runnable runnable);

在線程內調用cyclicBarrier的await()方法,當調用該方法的線程數達到指定size個數時,等待的線程纔會往下執行。

閉鎖CountDownLatch

CountDownLatch(int size)

也是線程同步的輔助類,指定的一個或多個線程等待其他線程執行完成後執行。

在需要等待的線程內調用await(),該線程就會暫停執行,在被等待的線程調用countdown(),countDown計數減一,直到0,等待線程繼續執行。

countDownLath無法被重置,循環柵欄可以。

循環柵欄是要執行的線程增加,countDownLatch是執行完成的線程增加。

同步集合

copyOnWriteArraylist,copyOncWriteArraySet

內部使用ReentrantLock進行同步,讀時不需要枷鎖,寫入時加鎖,將array中的元素複製,在新數組中添加,再將新數組寫入到原數組中。

ConcurrentHashMap,HashTable

HashTable在高併發情況下,效率低下原因是,所有線程公用一把鎖,get()也會被加鎖。

concurrentHashMap使用鎖分段技術,對數據分段枷鎖,減少了鎖的粒度,提高了效率。

BlockingQueue

阻塞隊列滿時添加元素線程將被阻塞,隊空時取元素的線程也進入等待。當需要實現生產消費者模型問題時(還可以用wait()和ReentrantLock),使用BlockingQueue,避免了手動判斷條件。

具體的api參見java文檔:http://docs.oracle.com/javase/8/docs/api/
其實現類包括:ArrayBlockingQueue,LinkedBlockingQueue等,參考api文檔

參考:

http://www.blogjava.net/syniii/articles/338254.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章