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文檔
參考: