Android 多线程之几个基本问题
Android中的進(jìn)程和線程
- Android中的一個(gè)應(yīng)用程序一般就對(duì)應(yīng)著一個(gè)進(jìn)程,多進(jìn)程的情況可以參考Android 多進(jìn)程通信之幾個(gè)基本問題
- Android中更常見的是多線程的情況,一個(gè)應(yīng)用程序中一般都有包括UI線程等多個(gè)線程。Android中規(guī)定網(wǎng)絡(luò)訪問必須在子線程中進(jìn)行,而操作更新UI則只能在UI線程。
- 常見的網(wǎng)絡(luò)請(qǐng)求庫(kù),如OkHttp、Volly等都為我們封裝好了線程池,所以我們?cè)谶M(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)一般不是很能直觀地感受到創(chuàng)建線程以及切換線程的過程。
- 線程是一種很寶貴的資源,要避免頻繁創(chuàng)建銷毀線程,一般推薦用線程池來管理線程。
線程的狀態(tài)
線程可能存在6種不同的狀態(tài):新創(chuàng)建(New)、可運(yùn)行(Runnable)、阻塞狀態(tài)(Blocked)、等待狀態(tài)(Waiting)、限期等待(Timed Waiting)、終止?fàn)顟B(tài)(Terminated)
- 新創(chuàng)建(New):創(chuàng)建后但還未啟動(dòng)的線程(還沒有調(diào)用start方法)處于這種狀態(tài)
- 可運(yùn)行(Runnable):一旦調(diào)用了start方法,線程就處于這種狀態(tài)。需要注意的是此時(shí)線程可能正在執(zhí)行,也可能在等待CPU分配執(zhí)行的時(shí)間
- 阻塞狀態(tài)(Blocked):表示線程被鎖阻塞,等待獲取到一個(gè)排他鎖。在程序等待進(jìn)入同步區(qū)域時(shí),線程將進(jìn)入這種狀態(tài)
-
等待狀態(tài)(Waiting):處于這種狀態(tài)的線程不會(huì)被分配CPU執(zhí)行時(shí)間,它們要等待被其他線程顯示地喚醒。調(diào)用以下方法會(huì)讓線程進(jìn)入這種狀態(tài):
- 沒有設(shè)置Timeout參數(shù)的Object.wait()方法
- 沒有設(shè)置Timeout參數(shù)的Thread.join()方法
-
限期等待(Timed Waiting):與等待狀態(tài)(Waiting)不同的是,處于這種狀態(tài)的線程不需要等待其它線程喚醒,在一定時(shí)間之后會(huì)由系統(tǒng)喚醒。調(diào)用以下方法會(huì)讓線程進(jìn)入這種狀態(tài):
- Thread.sleep()方法
- 設(shè)置了Timeout參數(shù)的Object.wait()方法
- 設(shè)置了Timeout參數(shù)的Thread.join()方法
-
終止?fàn)顟B(tài)(Terminated):表示線程已經(jīng)執(zhí)行完畢。導(dǎo)致線程終止有2種情況:
- 線程的run方法執(zhí)行完畢,正常退出
- 因?yàn)橐粋€(gè)沒有捕獲的異常而終止了run方法
創(chuàng)建線程
創(chuàng)建線程一般有如下幾種方式:繼承Thread類;實(shí)現(xiàn)Runnable接口;實(shí)現(xiàn)Callable接口
- 繼承Thread類,重寫run方法
- 實(shí)現(xiàn)Runnable接口,并實(shí)現(xiàn)run方法
-
實(shí)現(xiàn)Callable接口,重寫call方法
- Callable可以在任務(wù)接受后提供一個(gè)返回值而Runnable不行
- Callable的call方法可以拋出異常,Runnable的run方法不行
- 運(yùn)行Callable可以拿到一個(gè)Future對(duì)象,表示計(jì)算的結(jié)果,通過Future的get方法可以拿到異步計(jì)算的結(jié)果,不過當(dāng)前線程會(huì)阻塞。
- 以上三種方式就是常見的創(chuàng)建線程的方式。推薦使用實(shí)現(xiàn)Runnable接口的方法。
線程中斷
- 當(dāng)一個(gè)線程調(diào)用interrupt方法時(shí),線程的中斷標(biāo)識(shí)為將被設(shè)置成true
- 通過Thread.currentThread().isInterrupted()方法可以判斷線程是否應(yīng)該被中斷
- 可以通過調(diào)用Thread.interrupted()對(duì)中斷標(biāo)志位進(jìn)行復(fù)位(設(shè)置為false)
- 如果一個(gè)線程處于阻塞狀態(tài),線程在檢查中斷標(biāo)志位時(shí)如果發(fā)現(xiàn)中斷標(biāo)志位為true,則會(huì)在阻塞方法處拋出InterruptedException異常,并且在拋出異常前會(huì)將中斷標(biāo)志位復(fù)位,即重新設(shè)置為false
- 不要在代碼底層捕獲InterruptedException異常后不做處理
同步的幾種方法
同步的方式一般有如下3種:volatile關(guān)鍵字、synchronized關(guān)鍵字、重入鎖ReentrantLock
volatile關(guān)鍵字
- volatile關(guān)鍵字實(shí)現(xiàn)多線程安全關(guān)鍵在于它的可見性特性,但它需要滿足一些條件才能保證線程安全,具體可以查看文章深入理解Java虛擬機(jī)(八)之Java內(nèi)存模型
- 在用volatile關(guān)鍵字來實(shí)現(xiàn)多線程安全時(shí)需要注意volatile不保證原子性,也就是不能用于一些自增、自減等操作,也不能用于一些不變式中,自增、自減比較好理解,下面看看不變式的情況
- 上面的例子中,如果初始值是(0,5),線程A調(diào)用setLower(4),線程B調(diào)用setUpper(3),顯然最后結(jié)果就會(huì)變成(4,3)了
- volatile使用的場(chǎng)景常見的有作為狀態(tài)標(biāo)志以及DCL單例模式
synchronized關(guān)鍵字和重入鎖ReentrantLock
- synchronized關(guān)鍵字比較常見,可以用于同步方法也可以用于同步代碼塊,一般推薦用同步方法,同步代碼塊的安全性不高。
- 重入鎖ReentrantLock相比synchronized提供了一些獨(dú)有的特性:可以綁定多個(gè)解鎖的條件Condition、可以實(shí)現(xiàn)公平鎖、可以設(shè)置放棄等待獲取鎖的時(shí)間。
- 一個(gè)ReentrantLock有多個(gè)相關(guān)的Condition,調(diào)用Condition的await方法會(huì)讓當(dāng)前線程進(jìn)入該條件的等待集并阻塞,直到另一個(gè)線程調(diào)用了同一個(gè)條件的signalAll方法激活因?yàn)檫@個(gè)條件而進(jìn)入阻塞的所有線程
- 一般線程同步用得比較多的還是synchronized同步方法和一些java.util.concurrent包提供的一些類
如何安全的終止線程
雖然我們一般都是利用線程池來管理線程而不會(huì)直接顯示地創(chuàng)建線程,但是作為線程相關(guān)知識(shí)的一部分,我們還是要了解如何安全地終止一個(gè)線程。
要安全地終止一個(gè)線程,一般有2種方法:中斷和標(biāo)志位
(1)利用中斷來終止線程
Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {//do something}} });//當(dāng)我們調(diào)用Thread的interrupt方法時(shí),線程就會(huì)退出循環(huán)停止了。 thread.interrupt();(2)通過標(biāo)志位
private static class MyRunnable implements Runnable {//控制線程的標(biāo)志位,需要用 volatile關(guān)鍵字 private volatile boolean on = true;@Overridepublic void run() {while (on) {//do something }}public void cancel() {on = false;} }//啟動(dòng)線程 MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable);//終止線程 myRunnable.cancel();歡迎關(guān)注我的微信公眾號(hào),期待與你一起學(xué)習(xí),一起交流,一起成長(zhǎng)!
總結(jié)
以上是生活随笔為你收集整理的Android 多线程之几个基本问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: React开发(160):onref使用
- 下一篇: ASP.NET Core 基础教程 -