進(jìn)程/線程分別是啥耕突?
- 通俗地說(shuō)進(jìn)程和線程都是一個(gè)時(shí)間段的描述厂抽,是CPU工作時(shí)間段的描述。
- 默認(rèn)情況下,同一應(yīng)用程序下的所有組件運(yùn)行在同一進(jìn)程中埠况。(每個(gè)應(yīng)用程序都有一個(gè)自己的進(jìn)程)
- cpu的最小調(diào)度單位指的是線程(一個(gè)app最少有8個(gè)線程其中包括ui線程等)
- cpu的最小資源分配單位是進(jìn)程
Android哪里用到了耸携?
- 要說(shuō)哪里用到了?用到的地方還是很多的询枚!
- 舉個(gè)例子:ui線程這個(gè)常聽(tīng)到的詞违帆,處理耗時(shí)任務(wù)需不需要在子線程處理?下載總不能放在主線程等著吧金蜀?總之呢在android上一切耗時(shí)的操作都是可以放在子線程中去做的畢竟5s 10s 20s 沒(méi)響應(yīng) Activity BroadcastReceiver Service就沒(méi)響應(yīng)Anr掉了哦刷后。
實(shí)現(xiàn)多線程的N種方式(包含信息交換)
其實(shí)實(shí)現(xiàn)線程切換的方式非常的多,最方便的還是當(dāng)屬rxjava但是rxjava的使用沒(méi)那么輕量級(jí)渊抄,我們總不能為了單純一個(gè)切換線程做一下查詢(xún)數(shù)據(jù)庫(kù)操作就去引入rxjava吧尝胆?我們不能為了更新個(gè)ui就引入rxjava吧?如果項(xiàng)目中已經(jīng)使用了rxjava并且熟悉rxjava這看起來(lái)還是很愜意的护桦,但是如果沒(méi)有呢含衔?這就很笨重了。
所以啰嗦了這么多主要想說(shuō)的還是下面這些種用法還是有必要學(xué)習(xí)的二庵!
Handler
- handler原理就不闡述了贪染,有興趣的老哥可以看一下這篇簡(jiǎn)述
- 咱說(shuō)說(shuō)用法吧
主線程和子線程通信(下面代碼使用了兩種方式一種是直接post(runnable)--看thread0,另一種是sendMessage(message)--看thread1)
public class MainActivity extends ReactActivity {
Handler handler0 = new Handler();//主線程初始化Handler自動(dòng)綁定主線程
Handler handler1 = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
textView.setText((String) msg.obj);
break;
}
}
};
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new TestThread0().start();
new TestThread1().start();
}
class TestThread0 extends Thread {
@Override
public void run() {
super.run();
try {
Thread.sleep(5500);
handler0.post(new Runnable() {
@Override
public void run() {
textView.setText("OK");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TestThread1 extends Thread {
@Override
public void run() {
try {
Thread.sleep(5500);
Message message = new Message();
message.what = 001;
message.obj = "OK";
handler1.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
子線程和子線程通信
ps1:雙Thread形式
new Thread() {
@Override
public void run() {
//注意接受消息的線程一定要手動(dòng)開(kāi)啟Looper的循環(huán)催享,并且Looper要先準(zhǔn)備準(zhǔn)備完成后處理接收邏輯然后再開(kāi)啟循環(huán)杭隙。
Looper.prepare();
handler0 = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
System.out.print((String) msg.obj);
break;
}
}
};
Looper.loop();
}
}.start();
new Thread() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
message.obj = "from thread1";
handler0.sendMessage(message);
}
}.start();
ps2:使用ThreadHandler
HandlerThread handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
final Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
//再handlerThread線程進(jìn)行處理
switch (msg.what) {
case 1:
Log.d("test", "來(lái)自主線程的消息");
break;
case 2:
Log.d("test", "來(lái)自子線程的消息");
break;
}
}
};
handler.sendEmptyMessage(1);
new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(2);
}
}).start();
AsyncTask
- 其實(shí)AsyncTask的原理也不過(guò)就是handler+內(nèi)部維護(hù)的線程池進(jìn)行了一系列的封裝,它被設(shè)計(jì)出來(lái)的目的就是為了滿(mǎn)足Android的特殊需求:非主線程不能操作(UI)組件因妙,所以AsyncTask擴(kuò)展Thread增強(qiáng)了與主線程的交互的能力痰憎。如果你的應(yīng)用沒(méi)有與主線程交互,那么就直接使用Thread就好了攀涵。
- 注意下面AsyncTask的泛形參數(shù)铣耘,他們有自己的意義
public abstract class AsyncTask<Params, Progress, Result>
// 第一個(gè)params代表了初始參數(shù)由主線程傳入
// 第二個(gè)Progress代表了執(zhí)行進(jìn)度的返回參數(shù)
// 第三個(gè)Result代表了執(zhí)行結(jié)果的返回參數(shù)
// 如果不需要某一項(xiàng)直接填void就可以咯,或者完全不填就代表了全部void
- 使用的話看下面的代碼段吧
AsyncTask asyncTask = new AsyncTask() {
@Override
protected void onPreExecute() {
//顧名思義PreExecute再execute之前意味著這個(gè)方法中可以更新UI以故,在耗時(shí)操作執(zhí)行之前的操作蜗细。
super.onPreExecute();
}
@Override
protected Object doInBackground(Object[] objects) {
//這個(gè)是主要的方法,所有的耗時(shí)的操作需要在這個(gè)方法中處理怒详,
publishProgress("");
//調(diào)用publishProgress()方法來(lái)更新操作的進(jìn)度
return null;
}
@Override
protected void onProgressUpdate(Object[] values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Object o) {
//在耗時(shí)操作完成之后鳄乏,觸發(fā)這個(gè)方法,在UI線程中執(zhí)行棘利,可以通知用戶(hù)操作已經(jīng)完成
super.onPostExecute(o);
}
};
asyncTask.execute();
IntentService
- 要說(shuō)這個(gè)IntentService為啥放在了這個(gè)位置呢?因?yàn)檫@個(gè)東西別看它名字里面又有intent又有service它其實(shí)本質(zhì)上還是一個(gè)service朽缴,它繼承自service但是內(nèi)部維護(hù)了一個(gè)自己的線程這個(gè)線程是通過(guò)HandlerThread開(kāi)啟的(handlerThread本質(zhì)還是thread只是其內(nèi)部完成了Looper的初始化他還是要結(jié)合handler使用的)
- 我們使用了IntentService最起碼有兩個(gè)好處,一方面不需要自己去newThread了;另一方面不需要考慮在什么時(shí)候關(guān)閉該Service了(源碼調(diào)用stopSelf(msg.arg1))善玫。
- 下面我們看看這貨的基本使用吧
首先注意IntentService使用的是啟動(dòng)方式開(kāi)啟service
再次注意IntentService可以多次啟動(dòng),一但便會(huì)回掉再I(mǎi)ntentService的onHandleIntent方法,直到?jīng)]有需要處理的intent茅郎,該service會(huì)自己關(guān)閉蜗元。
最后手動(dòng)關(guān)掉IntentService可以使用ondestory
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this, MyIntentService.class);
Bundle bundle = new Bundle();
bundle.putString("1","one");
startService(intent);
}
class MyIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
//此處可以做點(diǎn)準(zhǔn)備工作什么的
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//此處已經(jīng)在IntentService開(kāi)啟的內(nèi)部線程處理了
Log.d("IntentServiceTest", (String) intent.getExtras().get("1"));
//此處怎么把處理好的消息返回給Ui線程呢??jī)煞N方法1:本地廣播2:handler(此處不述)
}
}
Thread(runnable/callable) + 線程池 + future/futuretask等
- 開(kāi)啟線程的最簡(jiǎn)單方式Thread
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//方法一
new Thread(new Runnable() {
@Override
public void run() {
Log.d("ThreadTest", "方法一:我在子線程");
}
}).start();
//方法二
new myThread().start();
}
//方法二
class myThread extends Thread {
@Override
public void run() {
Log.d("ThreadTest", "方法二:我在子線程");
}
}
直接顯示創(chuàng)建線程是一種很不明智的做法系冗,由于在創(chuàng)建線程和銷(xiāo)毀線程上花的事件以及系統(tǒng)資源的開(kāi)銷(xiāo)很大奕扣,有可能造成大量同類(lèi)線程從而導(dǎo)致消耗完內(nèi)存或者“過(guò)度切換”的問(wèn)題,總之不要顯示創(chuàng)建線程掌敬,為了避險(xiǎn)這些問(wèn)題java給我們提供了線程池
- 處理并發(fā)的最好方式線程池
最好不最好其實(shí)我也說(shuō)不準(zhǔn)惯豆,限于自己的姿勢(shì)水平目前處理多線程問(wèn)題都是適用線程池進(jìn)行管理的,而且Aaync內(nèi)部也是使用線程池管理了多個(gè)線程奔害,既然源碼都在用了楷兽,我想我們來(lái)使用它也不過(guò)分吧!
常用線程池
ExecutorService executorService0 = Executors.newSingleThreadExecutor();
//一個(gè)單線程化的線程池华临,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù)芯杀,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。(常用)
ExecutorService executorService1 = Executors.newCachedThreadPool();
//一個(gè)可緩存線程池雅潭,如果線程池長(zhǎng)度超過(guò)處理需要揭厚,可靈活回收空閑線程,若無(wú)可回收扶供,則新建線程筛圆。
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
//一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù)诚欠,超出的線程會(huì)在隊(duì)列中等待顽染。
ExecutorService executorService3 = Executors.newWorkStealingPool();
//創(chuàng)建持有足夠線程的線程池來(lái)支持給定的并行級(jí)別,并通過(guò)使用多個(gè)隊(duì)列轰绵,減少競(jìng)爭(zhēng)粉寞,它需要穿一個(gè)并行級(jí)別的參數(shù),如果不傳左腔,則被設(shè)定為默認(rèn)的CPU數(shù)量唧垦。
ScheduledExecutorService executorService4 = Executors.newScheduledThreadPool(10);
//一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行液样。(延遲執(zhí)行線程池)
ScheduledExecutorService executorService5 = Executors.newSingleThreadScheduledExecutor();
//創(chuàng)建一個(gè)單任務(wù)線程池振亮,支持定時(shí)及周期性任務(wù)執(zhí)行。(延遲執(zhí)行線程池)其實(shí)等于Executors.newScheduledThreadPool(1);
常規(guī)用法-延遲任務(wù)和循環(huán)任務(wù)
ScheduledExecutorService excutorService5 = Executors.newSingleThreadScheduledExecutor();
excutorService5.schedule(new Runnable() {
//延遲1s執(zhí)行
@Override
public void run() {
Log.d("ScheduledTest", "test");
}
}, 1, TimeUnit.SECONDS);
excutorService5.scheduleAtFixedRate(new Runnable() {
//延遲3s執(zhí)行鞭莽,每隔1s進(jìn)行一次循環(huán)
@Override
public void run() {
Log.d("ScheduledTest", "test1");
}
}, 3, 1, TimeUnit.SECONDS);
excutorService5.shutdown();
常規(guī)用法
//不需要返回值的常規(guī)用法
ExecutorService executorService0 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService0.execute(new Runnable() {
@Override
public void run() {
Log.d("executorTest", String.valueOf(finalI));
}
});
}
executorService0.shutdown();
//需要返回值的常規(guī)用法
ExecutorService executorService0 = Executors.newSingleThreadExecutor();
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int finalI = i;
Future future = executorService1.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
System.out.print(String.valueOf(finalI));
return finalI;
}
});
try {
System.out.print(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executorService0.shutdown();
for (int i = 0; i < 10; i++) {
Future future1 = executorService1.submit(new Runnable() {
@Override
public void run() {
}
}, "always one");
try {
System.out.print(future1.get());
//永遠(yuǎn)為“always one”這是因?yàn)閟ubmit方法指定了這個(gè)值坊秸。如若不指定返回null
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executorService1.shutdown();
補(bǔ)充
提交任務(wù)的方式一般兩種submit()(有返回值)和execute()(沒(méi)返回值)在不需要一個(gè)結(jié)果的時(shí)候直接用execute()會(huì)提升很多性能。因?yàn)閟ubmit()方法內(nèi)部會(huì)封裝RunnableFuture(實(shí)際返回FutureTask的實(shí)例化)然后在交給execute()執(zhí)行澎怒,注意runnable調(diào)用submit()也會(huì)有返回值但是為值null褒搔。
關(guān)閉任務(wù)一般分為兩個(gè)階段第一階段shutdown調(diào)用后,不可以再submit新的task,已經(jīng)submit的將繼續(xù)執(zhí)行星瘾。第二階段shutdownNow試圖停止當(dāng)前正執(zhí)行的task走孽,并返回尚未執(zhí)行的task的list。
線程池理論上來(lái)講其實(shí)有無(wú)數(shù)種我們一般都是直接在Executors里面拿琳状,但是如果看過(guò)源碼我們發(fā)現(xiàn)其實(shí)它最終也是根據(jù)不同的需要實(shí)例化了ThreadPoolExecutor而已磕瓷。
future.get()是阻塞方法,如果調(diào)用這個(gè)方法來(lái)獲得返回值念逞,那么在獲得返回值之前當(dāng)前線程池都會(huì)阻塞在這個(gè)單線程上(部分并發(fā)線程池會(huì)出現(xiàn)問(wèn)題)困食。
FutureTask是啥?它就是Future這個(gè)接口的唯一實(shí)現(xiàn)類(lèi)(java的多態(tài)嘛肮柜!面向接口編程嘛陷舅!所以submit()方法返回的其實(shí)是FutureTask的實(shí)例化對(duì)象),內(nèi)部封裝了很多方法审洞,可以幫助判斷線程的執(zhí)行狀態(tài),并且還可以取消一個(gè)線程的執(zhí)行莱睁。
FutureTask的一個(gè)實(shí)用方法
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
FutureTask futureTask = new FutureTask(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("任務(wù)執(zhí)行中" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()));
Thread.sleep(3000);
Random random = new Random();
return random.nextInt();
}
}) {
@Override
protected void done() {
//此處是自定義FutureTask帶來(lái)的一個(gè)小福利,我們可以重寫(xiě)他的done方法在Callable或者runnable的call和run方法執(zhí)行完后會(huì)執(zhí)行這個(gè)call方法在此處我們可以做一些事件處理
try {
int num = (int) this.get();
System.out.println("任務(wù)結(jié)束 結(jié)果是~~~~~~" + num + "~~~~~~" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
super.done();
}
};
System.out.println("任務(wù)開(kāi)始" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()));
executorService1.execute(futureTask);
線程同步問(wèn)題簡(jiǎn)略分析
- 首先說(shuō)下synchronized關(guān)鍵字
/**
* 一個(gè)單例模式的工具類(lèi)
*/
public class XXXTest {
private static XXXTest instance;
private static Object obj = new Object();
private XXXTest(){}
/**
* synchronized 修飾的同步方法
* */
public static synchronized XXXTest getInstance(){
if (instance == null){
instance = new XXXTest();
}
return instance;
}
/**
* 含有synchronized 同步快的方法
* */
public static XXXTest getInstance(){
if (instance == null){
synchronized (obj){
instance = new XXXTest();
}
}
return instance;
}
}
以上是synchronized的兩種用法:
1芒澜、synchronized修飾方法仰剿,表示不同線程訪問(wèn)相同對(duì)象的相同方法,必須要排隊(duì)痴晦,相當(dāng)于synchronized對(duì)這個(gè)對(duì)象上了鎖南吮,只能獲取這個(gè)對(duì)象的鎖的線程才能使用這個(gè)方法,使用完畢自動(dòng)釋放鎖誊酌。
2部凑、synchronized修改某一段代碼,指定這段代碼塊要同步的對(duì)象進(jìn)行上鎖解鎖碧浊。例如例子中的代碼涂邀,先去判斷intance是否初始化,沒(méi)有就對(duì)obj進(jìn)行上鎖箱锐,防止創(chuàng)建多次比勉,破壞了單例模式。
兩種用法優(yōu)點(diǎn)缺點(diǎn)分析:
1驹止、synchronized修飾方法浩聋,使用簡(jiǎn)單,但是效率低下臊恋,不需要同步的操作也被迫同步衣洁。
2、synchronized代碼塊抖仅,使用相對(duì)復(fù)雜坊夫,需要對(duì)功能邏輯有完整的了解毙替,但是僅僅是同步了某一塊代碼,效率也大幅提升践樱。
注意:synchronized代碼塊指定同步對(duì)象不能為空對(duì)象
- 再說(shuō)一下Thread自帶的wait()、notify()凸丸、notifyAll()方法
首先這幾個(gè)方法是Object類(lèi)提供的并不是Thread類(lèi)特有的拷邢,三個(gè)方法配套使用,wait() 使得線程進(jìn)入阻塞狀態(tài)屎慢,它有兩種形式瞭稼,一種允許 指定以毫秒為單位的一段時(shí)間作為參數(shù),另一種沒(méi)有參數(shù)腻惠,前者當(dāng)對(duì)應(yīng)的 notify() 被調(diào)用或者超出指定時(shí)間時(shí)線程重新進(jìn)入可執(zhí)行狀態(tài)环肘,后者則必須對(duì)應(yīng)的 notify() 被調(diào)用,notifyAll()則比較特殊它可以喚醒在此對(duì)象監(jiān)視器上等待的所有線程集灌。
作者簡(jiǎn)介:
就職于甜橙金融技術(shù)部悔雹,負(fù)責(zé)android開(kāi)發(fā)。