AsyncTask的線程優(yōu)化,我們先了解線程和它在java中的怎么使用的。然后分析android中的實現(xiàn)方法。在模擬實驗存在的問題。給出解決方法。
1.線程
單線程只有一個順序執(zhí)行流,多線程則可以包括多個順序執(zhí)行,多個順序流之間互不干擾。
1.1進程
進程是處于運行過程中的程序,并具有一定獨立功能,是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位。
特征:
- 獨立性:進程是系統(tǒng)中獨立存在的實體,可以擁有自己獨立的資源,每個進程都有自己私有的地址空間(獨立的代碼和數(shù)據(jù)空間)。但是進程間的切換會有較大的開銷。
- 動態(tài)性:進程是一個正在系統(tǒng)中動態(tài)指令集合。并在進程中加入了時間概念。進程具有自己的生命周期和各種不同的狀態(tài)。
- 并發(fā)性:多個進程可以在單個處理器上并發(fā)執(zhí)行,多個線程之間不會互相影響。并行指同一時刻,有多條指令在多個處理器上同時執(zhí)行。并發(fā)指在同一時刻,只能有一條指令執(zhí)行,但多個進程,使得在宏觀上具有多個進程同時執(zhí)行的效果。
1.2線程
線程也被稱作輕量級進程。線程是進程的執(zhí)行單元。就像進程在操作系統(tǒng)中的地位一樣,線程在程序中是獨立的,并發(fā)的執(zhí)行流。當進程被初始化之后,主線程就被創(chuàng)建了。通常一個程序只有一個主進程,但我們也可以在該進程內(nèi)創(chuàng)建多條順序執(zhí)行流,這些順序執(zhí)行流就是Thread,每條Thread也是互相獨立的。
- 一個線程可以擁有自己的堆,棧,自己的程序計算器和自己的局部變量,但不再擁有系統(tǒng)資源,它與父進程的其他線程共享該進程所有的全部資源。
- 多個進程共享父進程全部資源,因此我們必須確保一個進程不會妨礙同一進程的其他線程。
- 線程是獨立運行的,它并不知道進程中是否還有其他線程的存在。線程的運行是搶占式。當前運行的進程在任何時候都可能被掛起,以便另一個線程可以運行。
- 一個線程可以創(chuàng)建和撤銷另一個線程,同一個進程(Process)的多個線程(Thread)之間可以并發(fā)執(zhí)行。
2.Thread和Runnable
2.1 Thread創(chuàng)建和啟動
- 1.定義子Thread并重寫run()。
- 2.創(chuàng)建線程對象
- 3.線程對象用start方法啟動線程
static class FirstThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(this.getName() + " " + i);
}
}
}
2.2 實現(xiàn)Runnable接口創(chuàng)建線程類
1.定義實現(xiàn)Runnable接口的類,重寫run方法
public class SecondThread implements Runnable
2.創(chuàng)建Runnable實現(xiàn)類的對象,并以此作為Thread的target來創(chuàng)建Thread對象,這個Thread對象才是真正的線程對象。
SecondThread st = new SecondThread();
3.調(diào)用線程對象的start方法來啟動該線程
new Thread(st,"TXB").start();
2.3 Thread和Runnable比較
| 優(yōu)缺點 | 繼承Thread | 實現(xiàn)Runnable |
|---|---|---|
| 優(yōu)點 | 簡單,直接使用this.getName()來獲取當前線程(因為本身是一個線程類對象) | 1.只是實現(xiàn)了Runnable接口,還可以繼承其他類2.多個線程共享一個target對象,非常適合多個線程來處理同一份資源的情況 |
| 缺點 | 因為線程類已經(jīng)繼承了Thread,所以不能再繼承其他父類了 | 略微復雜,要使用Thread.currentThread()來獲取當前線程 |
2.4 線程的生命周期
- 新建(new)
- 就緒(runnable)
- 運行(running)
- 阻塞(blocked)
- 死亡(dead)

==搶占式調(diào)度策略==
系統(tǒng)會給每個可執(zhí)行的線程一小段的時間來處理任務(wù);當該時間段使用完,系統(tǒng)就會剝奪該線程所占據(jù)的資源,讓其他線程獲得執(zhí)行的機會。在選擇下一個線程時,系統(tǒng)會考慮線程的優(yōu)先級。
就緒和運行狀態(tài)之間的轉(zhuǎn)換通常不受程序控制,而是由系統(tǒng)線程調(diào)度所導致。
2.5 join
當在某個程序執(zhí)行流中A調(diào)用其他線程的join方法,A(調(diào)用join方法的那個線程)將被阻塞,知道join方法加入的join線程完成為止。
public class Join {
public static class JoinThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public static void main(String[] args) {
JoinThread jt = new JoinThread();
new Thread(jt,"TXB").start();
for(int i=0;i<100;i++){
System.out.println("i:" + i);
if(i == 20){
Thread joinThread = new Thread(jt, "joinThread");
joinThread.start();
try {
joinThread.join();
} catch (InterruptedException e) {
System.out.println("e:" + e);
}
}
}
}
}
上面程序一共有3條線程:
主線程開始之后啟動了名為“TXB”的線程,該子線程將會和main線程并發(fā)執(zhí)行
當主線程的循環(huán)變量i等于20時,啟動了名為“joinThread”的線程,然后這個線程join進了main線程。注意:此時“joinThread”不會和main線程并發(fā)執(zhí)行,而是main線程必須等該線程執(zhí)行結(jié)束后才可以向下執(zhí)行。在“joinThread”執(zhí)行時,實際上只有兩條子線程(“TXB” 和 “joinThread”)并發(fā)執(zhí)行,而main線程處于等待(阻塞)狀態(tài)知道“joinThread”執(zhí)行完。

2.6 后臺程序(Daemon Thread)
指在后臺運行的 線程,任務(wù)是為其他的線程提供服務(wù)。 JVM的垃圾回收線程就是典型的后臺線程。
特征:如果所有的前臺線程都死亡,那么后臺線程會自動死亡。當整個虛擬機中只剩下后臺線程時,程序就沒有繼續(xù)運行的必要了,所以虛擬機也就退出了。
設(shè)置指定線程為后臺線程: 調(diào)用Thread對象的setDaemon()方法
前臺線程創(chuàng)建的子線程默認是前臺線程,后臺線程創(chuàng)建的子線程默認是后臺線程
public class DaemonThread {
public static class Daemon implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public static void main(String[] args) {
Daemon d = new Daemon();
Thread t = new Thread(d, "DaemonThread");
t.setDaemon(true);
t.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
}
}
}

上面代碼在main方法里先將t設(shè)置為后臺線程,然后啟動該線程(==要將某個線程設(shè)置為后臺線程,必須在該線程啟動之前設(shè)置,setDaemon(true)必須在start()之前調(diào)用,否則會引發(fā)IllegalThreadStateException==)。本來該線程和ing該執(zhí)行到i = 99才會結(jié)束,但是實際上它無法運行到99,因為主線程(程序中唯一的前臺線程)運行結(jié)束后,JVM會自動退出,后臺線程也就自動死亡了。
2.7 sleep和yield
sleep方法:Thread類的靜態(tài)方法,讓當前正在執(zhí)行的線程暫停一段時間,并且進入阻塞狀態(tài)。當當前線程調(diào)用sleep方法進入阻塞狀態(tài)之后,在它sleep的時間里,它不會獲得執(zhí)行的機會。就算系統(tǒng)中沒有其他可運行的程序,處于sleep的線程也不會運行,因此sleep方法常用于暫停程序的運行。
static void sleep(long millis)
static void sleep(long millis,int nanos)
yield:和sleep有點類似,也是Thread類的一個靜態(tài)方法。它也可以讓當前正在執(zhí)行的線程暫停,但不會使線程阻塞,只是將線程轉(zhuǎn)入就緒狀態(tài)。
| sleep | yield |
|---|---|
| sleep暫停當前線程之后,會給其他線程執(zhí)行機會,并不考慮線程優(yōu)先級 | yield方法暫停當前線程之后,==只有和當前線程優(yōu)先級相同或者更高的處于就緒狀態(tài)(runnable)的線程才能有執(zhí)行的機會== |
| sleep方法會將線程轉(zhuǎn)入阻塞狀態(tài),知道經(jīng)過了設(shè)定的阻塞時間才會轉(zhuǎn)到就緒狀態(tài) | yield方法不會將線程轉(zhuǎn)入阻塞狀態(tài),它只是強制讓當前線程從運行狀態(tài)(runnig)轉(zhuǎn)到就緒狀態(tài)(runnable)。因此完全有可能某個線程調(diào)用yield暫停之后又馬上獲得CPU資源被執(zhí)行 |
| sleep方法會拋出InterruptedException異常 | 不拋異常 |
| sleep方法比yiled方法具有更好的移植性 | 通常不要依靠yield來控制并發(fā)線程的執(zhí)行 |
2.9生產(chǎn)者和消費者
死鎖出現(xiàn)的原因
產(chǎn)生死鎖必須同時滿足以下四個條件,只要其中任一條件不成立,死鎖就不會發(fā)生.
- 1.互斥條件:線程要求對所分配的資源進行排他性控制,即在一段時間內(nèi)某 資源僅為一個進程所占有.此時若有其他進程請求該資源.則請求進程只能等待.
- 2.不剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能由獲得該資源的線程自己來釋放(只能是主動釋放).
- 3.請求和保持條件:線程已經(jīng)保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他線程占有,此時請求線程被阻塞,但對自己已獲得的資源保持不放.
- 4.循環(huán)等待條件:存在一種線程資源的循環(huán)等待鏈,鏈中每一個線程已獲得的資源同時被鏈中下一個線程所請求。
// 產(chǎn)品
static class ProductObject {
// 線程操作變量可見
public volatile static String value;
}
// 生產(chǎn)者線程
static class Producer extends Thread {
Object lock;
public Producer(Object lock) {
this.lock = lock;
}
@Override
public void run() {
// 不斷生產(chǎn)產(chǎn)品
while (true) {
synchronized (lock) { // 互斥鎖
// 產(chǎn)品還沒有被消費,等待
if (ProductObject.value != null) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 產(chǎn)品已經(jīng)消費完成,生產(chǎn)新的產(chǎn)品
ProductObject.value = "NO:" + System.currentTimeMillis();
System.out.println("生產(chǎn)產(chǎn)品:" + ProductObject.value);
lock.notify(); // 生產(chǎn)完成,通知消費者消費
}
}
}
}
// 消費者線程
static class Consumer extends Thread {
Object lock;
public Consumer(Object lock) {
this.lock = lock;
}
@Override
public void run() {
while (true) {
synchronized (lock) {
// 沒有產(chǎn)品可以消費
if (ProductObject.value == null) {
// 等待,阻塞
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消費產(chǎn)品:" + ProductObject.value);
ProductObject.value = null;
lock.notify(); // 消費完成,通知生產(chǎn)者,繼續(xù)生產(chǎn)
}
}
}
}
3. Callabe , Future ,線程池
Callable是Runnable接口的增強版,Callable也提供了一個call()方法作為線程執(zhí)行體
call()方法可以有返回值
call()方法可以聲明拋出異常
Callable問題:
Callable接口并不是Runnable接口的子接口,而Thread的構(gòu)造方法里形參的類型Runnable,所以Callable對象不能直接做為Thread的target;而且call方法不能直接調(diào)用,而是作為線程執(zhí)行體被調(diào)用。
為了解決這幾個問題:
java提供了Future接口來代替Callable接口的call方法,并為Futrue接口提供一個FutureTast實現(xiàn)類,這個實現(xiàn)了Future接口,也實現(xiàn)了Runnable接口。
線程池
線程池在系統(tǒng)啟動時就創(chuàng)建了大量空閑的線程,程序?qū)⒁粋€Runnable對象傳給線程池,線程池就會啟動一條線程來執(zhí)行該對象的run方法,當run方法執(zhí)行結(jié)束之后,該線程不會死亡,而是再次返回線程池中成為空閑狀態(tài),等待執(zhí)行下一個Runnable對象的run方法。
Executors工廠類來生產(chǎn)線程池
| 方法 | 描述 |
|---|---|
| newCachedThreadPool() | 創(chuàng)建一個具有緩存功能的線程池,系統(tǒng)根據(jù)需要創(chuàng)建線程,這線程會被緩存在線程池中 |
| newFixedThreadPool(int nThreads) | 創(chuàng)建一個可重用的、具有固定線程數(shù)的線程池 |
| newSingleThreadExecutor() | 創(chuàng)建一個只有單線程的線程池,相當于newFixedThreadPool(int nThreads)傳入?yún)?shù)為1 |
| newScheduledThreadPool(int corePoolSize) | 創(chuàng)建具有固定線程數(shù)的線程池,可以在指定延遲后執(zhí)行線程任務(wù)。corePoolSize指池中所保存的線程數(shù),即使線程是空閑的也被保存在線程池里 |
| newSingleThreadScheduledExecutor() | 創(chuàng)建只有固定線程的線程池,可以在指定延遲后執(zhí)行線程任務(wù)。 |
前三個方法返回一個ExecutorService對象,該對象代表一個線程池,可以執(zhí)行Runnable或Callable對象所代表的線程。
后兩個方法返回一個ScheduledExecutorService,是ExecutorService的子類,可以在指定延遲后執(zhí)行線程任務(wù)。
使用線程池來執(zhí)行線程任務(wù)的步驟:
- 調(diào)用Executors類的靜態(tài)工廠方法創(chuàng)建一個ExecutorService對象,該對象代表一個線程池。
- 創(chuàng)建Runnable或Callable實現(xiàn)類的實例,作為線程任務(wù)
- 調(diào)用ExecutorService對象的submit方法來提交Runnable或Callable任務(wù)
- 當不想再提交任何任務(wù)時調(diào)用ExecutorService對象的shutdown方法來關(guān)閉線程池
public static void main(String[] args) {
Task work = new Task();
FutureTask<Integer> future = new FutureTask<Integer>(work){
//異步任務(wù)執(zhí)行完成,回調(diào)
@Override
protected void done() {
try {
System.out.println("done:"+get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
//線程池(使用了預(yù)定義的配置)
ExecutorService executor = Executors.newCachedThreadPool();
//executor.submit(future);
executor.execute(future);
// new Thread(future,"TXB").start();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
//取消異步任務(wù)
//future.cancel(true);
try {
//阻塞,等待異步任務(wù)執(zhí)行完畢
System.out.println(future.get()); //獲取異步任務(wù)的返回值
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
//異步任務(wù)
static class Task implements Callable<Integer>{
//返回異步任務(wù)的執(zhí)行結(jié)果
@Override
public Integer call() throws Exception {
int i = 0;
for (; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + "_"+i);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return i;
}
}
4.android中的AsyncTask
在android中調(diào)用流程

模擬android中的AsyncTask
public static void main(String[] args) {
int CPU_COUNT = Runtime.getRuntime().availableProcessors(); //可用的CPU個數(shù)
int CORE_POOL_SIZE = CPU_COUNT + 1; //5
int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; //9
int KEEP_ALIVE = 1;
//任務(wù)隊列(128)
final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
//線程工廠
ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
String name = "Thread #" + mCount.getAndIncrement();
System.out.println(name);
return new Thread(r, name);
}
};
//線程池
Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
//執(zhí)行異步任務(wù)
//如果當前線程池中的數(shù)量大于corePoolSize,緩沖隊列workQueue已滿,
//并且線程池中的數(shù)量等于maximumPoolSize,新提交任務(wù)由Handler處理。
//RejectedExecutionException
for (int i = 0; i < 200; i++) {
//相當于new AsyncTask().execute();
THREAD_POOL_EXECUTOR.execute(new MyTask());
}
}
static class MyTask implements Runnable{
@Override
public void run() {
//System.out.println(Thread.currentThread().getName());
while(true){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

會出現(xiàn)這問題:這個問題產(chǎn)生的原因是默認情況任務(wù)隊列只分配了128,如果我們生存了200個任務(wù),就會出現(xiàn)這個問題。還有一個關(guān)鍵是任務(wù)阻塞了,也就是很耗時的時候。
怎么解決了:我們可以進行線程池擴容
Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(25);
在android中調(diào)用異步任務(wù)時使用new MyTask().executeOnExecutor(exec);
在這有可能出現(xiàn)內(nèi)存泄露的問題。
產(chǎn)生的原因是如果在子線程中一直處理一些事情,時間比較長,activity在旋轉(zhuǎn)或退出的時候,這個線程還好執(zhí)行,并保存activity的引用,讓其無法釋放。
解決方法:
@Override
protected void onDestroy() {
super.onDestroy();
task.cancel(true); // 取消任務(wù)
}
class MyTask extends AsyncTask<Void, Integer, Void> {
int i = 0;
@Override
protected Void doInBackground(Void... params) {
while (!isCancelled()) {
// Log.d("jason", String.valueOf(i++));
SystemClock.sleep(1000);
}
return null;
}
}