Android的應(yīng)用層都是Java寫的脚作,所以很多語法上的東西就是Java的,只不過某些類在原有基礎(chǔ)上進行了包裝,畢竟android跟java web兢孝,se之類的生產(chǎn)環(huán)境不一樣浇坐,而線程又是java中一塊很重要的知識睬捶,線程控制的好不好也關(guān)系到應(yīng)用性能提升大不大,畢竟不是Java出身近刘,所以簡單的說說java的線程
多線程
扯到線程擒贸,就不免會說道進程,簡答來數(shù)進程就是內(nèi)存中運行的應(yīng)用程序有獨立的內(nèi)存空間觉渴,而線程則是進程的組成介劫,線程即達成目標(biāo)的一個任務(wù)。
那么我們?yōu)槭裁词褂枚嗑€程呢案淋?如今cpu已經(jīng)進入4核座韵,八核時代,如何充分利用硬件資源去實現(xiàn)一個程序催生了多線程踢京。
舉個簡單的例子誉碴,運一批貨,在一個雙車道的道路上瓣距,如果按順序發(fā)車的話黔帕,貨物到達肯定要慢點,同時發(fā)貨的話蹈丸,理想情況時間就少了一半成黄。
Java的多線程
一般Java中實現(xiàn)多線程無非兩種方式:
- 繼承Thread
class NewThread extends Thread {
@Override
public void run() {
super.run();
}
}
- new Thread將任務(wù)放到Runnable里面
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
需要注意的是,調(diào)用start方法后逻杖,線程不是立即開啟奋岁,而是等待系統(tǒng)分配時間片(由于系統(tǒng)是隨機分配時間片,所以線程的調(diào)度存在不確定性)
線程狀態(tài)
- 新建狀態(tài)(New):**當(dāng)線程對象對創(chuàng)建后荸百,即進入了新建狀態(tài)闻伶,如:Thread t = new MyThread();
- 就緒狀態(tài)(Runnable):**當(dāng)調(diào)用線程對象的start()方法(t.start();),線程即進入就緒狀態(tài)管搪。處于就緒狀態(tài)的線程虾攻,只是說明此線程已經(jīng)做好了準(zhǔn)備铡买,隨時等待CPU調(diào)度執(zhí)行,并不是說執(zhí)行了t.start()此線程立即就會執(zhí)行霎箍;
- 運行狀態(tài)(Running):**當(dāng)CPU開始調(diào)度處于就緒狀態(tài)的線程時奇钞,此時線程才得以真正執(zhí)行,即進入到運行狀態(tài)漂坏。注:就 緒狀態(tài)是進入到運行狀態(tài)的唯一入口景埃,也就是說,線程要想進入運行狀態(tài)執(zhí)行顶别,首先必須處于就緒狀態(tài)中谷徙;
- 阻塞狀態(tài)(Blocked):處于運行狀態(tài)中的線程由于某種原因,暫時放棄對CPU的使用權(quán)驯绎,停止執(zhí)行完慧,此時進入阻塞狀態(tài),直到其進入到就緒狀態(tài)剩失,才 有機會再次被CPU調(diào)用以進入到運行狀態(tài)屈尼。根據(jù)阻塞產(chǎn)生的原因不同,阻塞狀態(tài)又可以分為三種:
1.等待阻塞:運行狀態(tài)中的線程執(zhí)行wait()方法拴孤,使本線程進入到等待阻塞狀態(tài)脾歧;
2.同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀態(tài)演熟;
3.其他阻塞 -- 通過調(diào)用線程的sleep()或join()或發(fā)出了I/O請求時鞭执,線程會進入到阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時芒粹、join()等待線程終止或者超時兄纺、或者I/O處理完畢時,線程重新轉(zhuǎn)入就緒狀態(tài)化漆。 - 死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法囤热,該線程結(jié)束生命周期。
線程優(yōu)先級
Java的線程是具有優(yōu)先級的获三,可以通過setPriority()設(shè)置線程的優(yōu)先級,控制線程的重要性(擁有較大機會獲取系統(tǒng)調(diào)度)锨苏,Thread有3個控制線程優(yōu)先級的常量
- static int MAX_PRIORITY ----10
- static int MIN_PRIORITY-----1
- static int NORM_PRIORITY---5
常見線程常用函數(shù)
1.sleep(long mills):讓當(dāng)前線程休眠指定的毫秒數(shù)
2.join():讓主線程等待調(diào)用該函數(shù)的線程結(jié)束再執(zhí)行(所在線程等待調(diào)用該方法的線程變量所在的線程執(zhí)行完畢)
應(yīng)用情形:當(dāng)主線程需要實時使用子線程的處理結(jié)果疙教,有點像阻塞式的編程
3.yield():暫停當(dāng)前線程,讓出時間片
實質(zhì)是將該線程調(diào)回到runnable狀態(tài)重新等待調(diào)度伞租,高優(yōu)先級線程會被先調(diào)用贞谓,同等級無法保證讓出時間片(進入了統(tǒng)一等待隊列,系統(tǒng)還是會選中當(dāng)前線程)
4.setPriority(int priority):上面有說過
5.interrupt():野蠻方式中斷線程葵诈,可能會導(dǎo)致資源無法釋放
6.wait():等待擁有同樣資源鎖的線程的執(zhí)行裸弦,線程同步的一個方法祟同,主要關(guān)系到對資源鎖定問題,后面討論
7.notify():喚醒擁有同樣資源鎖的線程
備注:sleep()與wait()區(qū)別:sleep的同時不釋放同步鎖理疙,也就是說sleep的同時擁有同樣資源鎖的線程不會拿到時間片,而wait()的時候是釋放同步鎖晕城,讓其他擁有同等資源鎖的線程獲得時間片(必須放在synchronized塊內(nèi))
線程同步問題
這個應(yīng)該是線程里面最重要,也是最難的部分了窖贤,主要由于系統(tǒng)調(diào)度的不確定性引起砖顷,一般不做線程同步的話,出現(xiàn)了某些問題赃梧,調(diào)試都很難調(diào)試出來
數(shù)據(jù)的同步在數(shù)據(jù)庫里面非常常見滤蝠,設(shè)想線程A,B同時對一個變量進行++操作
int res = 0;
class A extends Thread {
@Override
public void run() {
super.run();
res=res+1;
System.out.print("A:res" + res);
}}
class B extends Thread {
@Override
public void run() {
super.run();
res=res+1;
System.out.print("B:res" + res); }}```
上述代碼會有兩種不同的輸出
A:1
B:2
或者
B:1
A:2
這可能跟我預(yù)想的結(jié)果就不一樣了,因此我們需要對資源進行同步
######synshronized關(guān)鍵字
這是用來加同步鎖的關(guān)鍵字授嘀,他有兩種作用域
1. 對象:某個對象內(nèi)synchronized methodA(){}物咳,可以鎖住這個對象的該方法(若該對象擁有多個synchronized方法,加鎖后其它synchronized方法也無法調(diào)用)蹄皱,但不影響不同對象
2. 類:synchronized static methodA(){},對所有對象起效
除了方法前可以加synchronized览闰,某個區(qū)塊也可以加,他們的實質(zhì)都是鎖對象
```synchronized methodA(){} //鎖對象
synchronized static methodA(){} //鎖類```
等同于
```methodA(){synchronized(obj){} } //鎖對象
methodA(){
synchronized(obj.class){}}```
synchronized搭配wait(),notify()方法使用,wait()調(diào)用后夯接,不執(zhí)行wait()后面的代碼焕济,notify()后繼續(xù)執(zhí)行上次wait()后的代碼
######lock同步鎖
方便的解決同步問題的另一方案
class X {
// 顯示定義Lock同步鎖對象,此對象與共享資源具有一對一關(guān)系
private final Lock lock = new ReentrantLock();
public void m(){
// 加鎖 lock.lock();
//... 需要進行線程安全同步的代碼
// 釋放Lock鎖
lock.unlock(); } }
可以參考下Condition實現(xiàn)
######線程數(shù)據(jù)傳遞
由于線程的不確定性盔几,所以線程間的通信晴弃,也不像普通的同步模式下的通信
1. 構(gòu)造方法傳遞數(shù)據(jù) 由于構(gòu)造方法是線程一開始就傳入所以不存在同步問題,可通過重載構(gòu)造方法傳入?yún)?shù)
2. 通過公開方法賦值逊拍,類的屬性器賦值或者讀取
前兩個都建立在宿主線程與被調(diào)線程的通信上鞠,下面就是線程與線程之間(并無直接關(guān)聯(lián))
3. 通過同步控制,訪問共有對象(synchronized芯丧,wait芍阎,notify等)
4. 管道流(這個不是很了解,望高手指教)
---
接下來就談?wù)凴unnable缨恒,Callable<V>,Future谴咸。
由于Runnable里面的run函數(shù)返回為void,因此我們無法觀察任務(wù)的狀態(tài)骗露,Callable<V>正好解決了這個問題
public interface Callable<V> {
/**
- Computes a result, or throws an exception if unable to do so.
- @return computed result
- @throws Exception if unable to compute a result
*/
V call() throw Exception;
}```
Future又是什么東西呢,
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException,ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}```
* cancel取消任務(wù)
* isCancelled 返回任務(wù)是否被取消
* get 獲取任務(wù)這行結(jié)果岭佳,阻塞方法,等到任務(wù)正常完成才會返回值
* get(long萧锉,TimeUnit)在規(guī)定時間內(nèi)無返回結(jié)果就返回null
所以Future只是個接口因此我們只能使用它的實現(xiàn)
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}```
FutrueTask實現(xiàn)了RunnableFuture<v>,RunnableFuture<v>又繼承自Runnable珊随,F(xiàn)uture<v>,Callable<v>經(jīng)常搭配ExcutorService一起使用
public FutureTask(Callable<V> callable) {}
public FutureTask(Runnable runnable, V result) {}```
FutureTask提供了兩個構(gòu)造函數(shù),實質(zhì)都是實現(xiàn)Runnable與Future<v>接口,[FutureTask,Callable<V>使用](http://www.cnblogs.com/dolphin0520/p/3949310.html)
----
寫作思路大抵發(fā)散性的 有相關(guān)的就查閱下叶洞,篇幅比較長鲫凶,若有錯誤請大家指正,下次接著寫線程池