進(jìn)程與線(xiàn)程
- 進(jìn)程:進(jìn)程就是正在執(zhí)行的程序。
-
線(xiàn)程:是程序執(zhí)行的一條路徑, 一個(gè)進(jìn)程中可以包含多條線(xiàn)程遂跟。
通俗理解:例如你打開(kāi)微信就是打開(kāi)一個(gè)進(jìn)程,在微信里面和好友視頻聊天就是開(kāi)啟了一條線(xiàn)程。 -
兩者之間的關(guān)系:
一個(gè)進(jìn)程里面可以有多條線(xiàn)程幻锁,至少有一條線(xiàn)程凯亮。
一條線(xiàn)程一定會(huì)在一個(gè)進(jìn)程里面。
關(guān)于進(jìn)程與線(xiàn)程的講解越败,這篇文章講的挺好的-->進(jìn)程與線(xiàn)程的一個(gè)簡(jiǎn)單解釋
創(chuàng)建線(xiàn)程的三種方式
一触幼、繼承Thread
1硼瓣、定義一個(gè)類(lèi)MyThread繼承Thread究飞,并重寫(xiě)run方法。
2堂鲤、將要執(zhí)行的代碼寫(xiě)在run方法中亿傅。
3、創(chuàng)建該類(lèi)的實(shí)例瘟栖,并調(diào)用start()方法開(kāi)啟線(xiàn)程葵擎。
代碼如下:
public class MainActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//3、創(chuàng)建該類(lèi)的實(shí)例半哟,并調(diào)用start()方法開(kāi)啟線(xiàn)程酬滤。
MyThread myThread = new MyThread();
myThread.start();
}
//1、定義一個(gè)類(lèi)MyThread繼承Thread寓涨,并重寫(xiě)run方法盯串。
class MyThread extends Thread {
public void run() {
//2、將執(zhí)行的代碼寫(xiě)在run方法中戒良。
for (int i = 0; i < 100; i++) {
Log.d(TAG, "線(xiàn)程名字:" + Thread.currentThread().getName() + " i=" + i);
}
}
}
}
二体捏、實(shí)現(xiàn)Runnable接口
1、定義一個(gè)類(lèi)MyRunnable實(shí)現(xiàn)Runnable接口糯崎,并重寫(xiě)run方法几缭。
2、將要執(zhí)行的代碼寫(xiě)在run方法中沃呢。
3年栓、創(chuàng)建Thread對(duì)象, 傳入MyRunnable的實(shí)例,并調(diào)用start()方法開(kāi)啟線(xiàn)程薄霜。
代碼如下:
public class MainActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//3韵洋、創(chuàng)建Thread對(duì)象, 傳入MyRunnable的實(shí)例,并調(diào)用start()方法開(kāi)啟線(xiàn)程黄锤。
Thread thread = new Thread(new MyRunnable());
thread.start();
}
//1搪缨、定義一個(gè)類(lèi)MyRunnable實(shí)現(xiàn)Runnable接口,并重寫(xiě)run方法鸵熟。
class MyRunnable implements Runnable {
public void run() {
//2副编、將執(zhí)行的代碼寫(xiě)在run方法中。
for (int i = 0; i < 100; i++) {
Log.d(TAG, "線(xiàn)程名字:" + Thread.currentThread().getName() + " i=" + i);
}
}
}
}
三流强、實(shí)現(xiàn)Callable接口
Callable是類(lèi)似于Runnable的接口痹届,實(shí)現(xiàn)Callable接口的類(lèi)和實(shí)現(xiàn)Runnable的類(lèi)都是可被其它線(xiàn)程執(zhí)行的任務(wù)呻待。
1、自定義一個(gè)類(lèi)MyCallable實(shí)現(xiàn)Callable接口队腐,并重寫(xiě)call()方法
2蚕捉、將要執(zhí)行的代碼寫(xiě)在call()方法中
3、創(chuàng)建線(xiàn)程池對(duì)象柴淘,調(diào)用submit()方法執(zhí)行MyCallable任務(wù)迫淹,并返回Future對(duì)象
4、調(diào)用Future對(duì)象的get()方法獲取call()方法執(zhí)行完后的值
代碼如下:
public class MainActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//3为严、創(chuàng)建線(xiàn)程池對(duì)象敛熬,調(diào)用submit()方法執(zhí)行MyCallable任務(wù),并返回Future對(duì)象
ExecutorService pool = Executors.newSingleThreadExecutor();
Future<Integer> f1 = pool.submit(new MyCallable());
//4第股、調(diào)用Future對(duì)象的get()方法獲取call()方法執(zhí)行完后的值
try {
Log.d(TAG, "sum=" + f1.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//關(guān)閉線(xiàn)程池
pool.shutdown();
}
//1应民、自定義一個(gè)類(lèi)MyCallable實(shí)現(xiàn)Callable接口,并重寫(xiě)call()方法
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//2夕吻、將要執(zhí)行的代碼寫(xiě)在call()方法中
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
}
創(chuàng)建線(xiàn)程的三種方式對(duì)比
一诲锹、繼承Thread類(lèi)與實(shí)現(xiàn)Runnable接口的區(qū)別
我們都知道java支持單繼承,多實(shí)現(xiàn)涉馅。實(shí)現(xiàn)Runnable接口還可以繼承其他類(lèi)蓝丙,而使用繼承Thread就不能繼承其他類(lèi)了瞎访。所以當(dāng)你想創(chuàng)建一個(gè)線(xiàn)程又希望繼承其他類(lèi)的時(shí)候就該選擇實(shí)現(xiàn)Runnable接口的方式穗椅。
二全肮、實(shí)現(xiàn)Callable接口與Runnable接口的區(qū)別
Callable執(zhí)行的方法是call() ,而Runnable執(zhí)行的方法是run()盐捷。
call() 方法有返回值還能拋出異常偶翅, run() 方法則沒(méi)有沒(méi)有返回值,也不能拋出異常碉渡。
多線(xiàn)程
一聚谁、概念
一個(gè)進(jìn)程中開(kāi)啟了不止一個(gè)線(xiàn)程。
二滞诺、多線(xiàn)程的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
1形导、提高CPU的使用率
例如朋友圈發(fā)表圖片,當(dāng)你上傳9張圖片的時(shí)候习霹,如果開(kāi)啟一個(gè)線(xiàn)程用同步的方式一張張上傳圖片朵耕,假設(shè)每次上傳圖片的線(xiàn)程只占用了CPU 1%d的資源,剩下的99%資源就浪費(fèi)了淋叶。但是如果你開(kāi)啟9個(gè)線(xiàn)程同時(shí)上傳圖片阎曹,CPU就可以使用9%的資源了。
2、提高程序的工作效率
還是拿朋友圈發(fā)表圖片來(lái)說(shuō)处嫌,假設(shè)開(kāi)啟一個(gè)線(xiàn)程上傳一張圖片的時(shí)間是1秒栅贴,那么同步的方式上傳9張就需要9秒,但是你開(kāi)啟9個(gè)線(xiàn)程同時(shí)上傳圖片熏迹,那么就只需要1秒就完成了檐薯。缺點(diǎn)
1、如果有大量的線(xiàn)程,會(huì)影響性能,因?yàn)镃PU需要在它們之間切換注暗。
2坛缕、更多的線(xiàn)程需要更多的內(nèi)存空間。
3友存、多線(xiàn)程操作可能會(huì)出現(xiàn)線(xiàn)程安全或者死鎖等問(wèn)題祷膳。
三陶衅、多線(xiàn)程并行和并發(fā)的區(qū)別
概念
并行:多個(gè)處理器或者多核處理器同時(shí)執(zhí)行多個(gè)不同的任務(wù)屡立。
并發(fā):一個(gè)處理器處理多個(gè)任務(wù)。打個(gè)比喻
并行就是一個(gè)人用他的左手喂女兒吃飯搀军,同時(shí)用右手喂兒子吃飯。
并發(fā)就是一個(gè)人先喂女兒吃一口飯罩句,接著喂兒子吃一口焚刺,然后再喂女兒吃一口,輪流喂。舉個(gè)多線(xiàn)程并發(fā)操作同一數(shù)據(jù)出現(xiàn)線(xiàn)程安全的例子
利用多線(xiàn)程上傳9張圖片坡脐,并提示還剩幾張圖片未上傳。代碼如下:
public class MainActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
//圖片數(shù)量
private int mImgNum;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void multiThread(View view) {
mImgNum = 9;
//開(kāi)啟3條線(xiàn)程上傳圖片
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
}
public class MyRunnable implements Runnable {
@Override
public void run() {
while (true) {
if (mImgNum == 0) {
Log.d(TAG, Thread.currentThread().getName() + "全部上傳成功");
break;
}
try {
//模擬上傳一張圖片需要1秒鐘的時(shí)間
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mImgNum--;
Log.d(TAG, Thread.currentThread().getName() + "上傳了一張圖片...捅暴,還剩" + mImgNum + "張圖片未上傳");
}
}
}
}
打印結(jié)果如下:
由圖可知恬砂,圖片數(shù)量出現(xiàn)了負(fù)數(shù),顯然是錯(cuò)誤的蓬痒。
原因:
出現(xiàn)這種錯(cuò)誤的原因是有多個(gè)線(xiàn)程在操作共享的數(shù)據(jù)泻骤。即一個(gè)線(xiàn)程在操作共享數(shù)據(jù)的過(guò)程中CPU切換到其他線(xiàn)程又對(duì)該數(shù)據(jù)進(jìn)行操作,這就是所謂的多線(xiàn)程并發(fā)。
解決:
把操作數(shù)據(jù)的那段代碼用synchronized進(jìn)行同步, 這樣就能保證在同一時(shí)刻只能有一個(gè)線(xiàn)程能夠訪問(wèn)瞪讼。
代碼如下:
public class MainActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
//圖片數(shù)量
private int mImgNum;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void multiThread(View view) {
mImgNum = 9;
//開(kāi)啟3條線(xiàn)程上傳圖片
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
}
public class MyRunnable implements Runnable {
@Override
public void run() {
while (true) {
//加上synchronized進(jìn)行同步钧椰,保證在同一時(shí)刻只能有一個(gè)線(xiàn)程能夠訪問(wèn)
synchronized (MyRunnable.class) {
if (mImgNum == 0) {
Log.d(TAG, Thread.currentThread().getName() + "全部上傳成功");
break;
}
try {
//模擬上傳一張圖片需要1秒鐘的時(shí)間
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mImgNum--;
Log.d(TAG, Thread.currentThread().getName() + "上傳了一張圖片...,還剩" + mImgNum + "張圖片未上傳");
}
}
}
}
}
打印結(jié)果如下:
由圖可知符欠,圖片數(shù)量正常了嫡霞,但是發(fā)現(xiàn)一秒鐘只上傳了一張圖片,而且只有一個(gè)線(xiàn)程在上傳(每次while循環(huán)希柿, 哪個(gè)線(xiàn)程先訪問(wèn)是隨機(jī)的诊沪,偶爾會(huì)出現(xiàn)3個(gè)線(xiàn)程都在上傳的情況,線(xiàn)程休眠時(shí)間設(shè)置為100毫秒比較容易復(fù)現(xiàn))曾撤,并沒(méi)有實(shí)現(xiàn)并發(fā)端姚。其實(shí)這里的確解決了多線(xiàn)程并發(fā)操作同一數(shù)據(jù)出現(xiàn)線(xiàn)程安全的問(wèn)題,但是因?yàn)檫@里要模擬上傳圖片挤悉,把休眠時(shí)間放到了synchronized中渐裸,所以導(dǎo)致沒(méi)有并發(fā),用線(xiàn)程池就不會(huì)有這個(gè)問(wèn)題装悲,這個(gè)放到后面去講昏鹃。
線(xiàn)程池
關(guān)于線(xiàn)程池
前面舉的朋友圈發(fā)表圖片的多線(xiàn)程例子中,為了提高CPU的使用率和程序的工作效率就需要?jiǎng)?chuàng)建9個(gè)線(xiàn)程來(lái)上傳圖片诀诊。但是線(xiàn)程的創(chuàng)建和銷(xiāo)毀是非常耗CPU和內(nèi)存的洞渤,因?yàn)樗婕暗揭c操作系統(tǒng)進(jìn)行交互。這樣就可能導(dǎo)致創(chuàng)建與銷(xiāo)毀線(xiàn)程的開(kāi)銷(xiāo)比實(shí)際業(yè)務(wù)還大属瓣,而線(xiàn)程池就能很好的解決這些問(wèn)題载迄。線(xiàn)程池里的每一個(gè)線(xiàn)程結(jié)束后,并不會(huì)銷(xiāo)毀(可以設(shè)置超時(shí)銷(xiāo)毀)抡蛙,而是回到線(xiàn)程池中成為空閑狀態(tài)护昧,等待下一個(gè)對(duì)象來(lái)使用。
使用線(xiàn)程池的優(yōu)點(diǎn)
- 可以重用線(xiàn)程池中的線(xiàn)程溜畅,避免因?yàn)榫€(xiàn)程的創(chuàng)建和銷(xiāo)毀所帶來(lái)的性能開(kāi)銷(xiāo)捏卓。
- 能有效控制線(xiàn)程池的最大并發(fā)數(shù),避免大量的線(xiàn)程之間因互相搶占系統(tǒng)資源而導(dǎo)致的阻塞現(xiàn)象慈格。
- 能夠?qū)€(xiàn)程進(jìn)行簡(jiǎn)單的管理(關(guān)閉怠晴、回收等),并提供定時(shí)執(zhí)行以及指定間隔循環(huán)執(zhí)行等功能浴捆。
線(xiàn)程池中重要的幾個(gè)類(lèi)
-
Executor:java中線(xiàn)程池的頂級(jí)接口蒜田,可以稱(chēng)它為一個(gè)執(zhí)行器,通過(guò)查看源碼也知道选泻,他只有一個(gè)簡(jiǎn)單的方法execute(Runnable command)冲粤,就是用來(lái)執(zhí)行提交的任務(wù)美莫。源碼如下:
【Executor.java】
public interface Executor {
void execute(Runnable command);
}
- ExecutorService:Executor的子類(lèi),也是真正的線(xiàn)程池接口梯捕。它提供了提交任務(wù)和關(guān)閉線(xiàn)程池等方法厢呵。調(diào)用submit方法提交任務(wù)還可以返回一個(gè)Future對(duì)象,利用該對(duì)象可以了解任務(wù)執(zhí)行情況傀顾,獲得任務(wù)的執(zhí)行結(jié)果或取消任務(wù)襟铭。
- Executors:由于線(xiàn)程池配置比較復(fù)雜,自己配置的線(xiàn)程池可能性能不是最好的短曾。Executors就是用來(lái)方便創(chuàng)建各種常用線(xiàn)程池的工具類(lèi)寒砖。
-
ThreadPoolExecutor:ExecutorService的默認(rèn)實(shí)現(xiàn),Executors創(chuàng)建各種線(xiàn)程池的時(shí)候內(nèi)部其實(shí)就是調(diào)用了ThreadPoolExecutor的構(gòu)造方法嫉拐。下面通過(guò)查看源碼驗(yàn)證哩都。
例如隨便創(chuàng)建一個(gè)線(xiàn)程池:
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
點(diǎn)擊newCachedThreadPool()進(jìn)去,里面確實(shí)調(diào)用了ThreadPoolExecutor的構(gòu)造方法婉徘,如下:
【Executor.java】
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ThreadPoolExecutor構(gòu)造函數(shù)參數(shù)說(shuō)明
下面從源碼中拿一個(gè)參數(shù)最完整的來(lái)講解漠嵌,如下:
【ThreadPoolExecutor.java】
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
- corePoolSize:核心線(xiàn)程數(shù),如果運(yùn)行的線(xiàn)程數(shù)少于corePoolSize判哥,當(dāng)有新的任務(wù)過(guò)來(lái)時(shí)會(huì)創(chuàng)建新的線(xiàn)程來(lái)執(zhí)行這個(gè)任務(wù)献雅,即使線(xiàn)程池中有其他空閑的線(xiàn)程碉考。
- maximumPoolSize:線(xiàn)程池中允許的最大線(xiàn)程數(shù)塌计。
- keepAliveTime:如果線(xiàn)程數(shù)多于核心線(xiàn)程數(shù),那么這些多出來(lái)的線(xiàn)程如果空閑時(shí)間超過(guò)keepAliveTime將會(huì)被終止侯谁。
- unit:keepAliveTime參數(shù)的時(shí)間單位锌仅。
- workQueue:任務(wù)隊(duì)列,通過(guò)線(xiàn)程池的execute方法會(huì)將任務(wù)Runnable存儲(chǔ)在隊(duì)列中墙贱。
- threadFactory:線(xiàn)程工廠热芹,用來(lái)創(chuàng)建新線(xiàn)程。
- handler:添加任務(wù)出錯(cuò)時(shí)的策略捕獲器惨撇,默認(rèn)是ThreadPoolExecutor.AbortPolicy 伊脓,即添加任務(wù)出錯(cuò)就直接拋出異常 。
四種線(xiàn)程池
-
FixedThreadPool:線(xiàn)程數(shù)量固定的線(xiàn)程池魁衙,空閑的線(xiàn)程不會(huì)被回收报腔,超出的線(xiàn)程會(huì)在隊(duì)列中等待。由于它只有核心線(xiàn)程并且不會(huì)被回收剖淀,所以能夠快速響應(yīng)外界的請(qǐng)求纯蛾。
例子:創(chuàng)建線(xiàn)程數(shù)為3的線(xiàn)程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int finalI = i;
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
Log.d(TAG, "線(xiàn)程名字:" + Thread.currentThread().getName() + " i=" + finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
打印結(jié)果:
由打印結(jié)果可知,10個(gè)任務(wù)始終在3個(gè)線(xiàn)程中執(zhí)行纵隔。
-
SingleThreadExecutor:只有一個(gè)核心線(xiàn)程的線(xiàn)程池翻诉,這樣能保證所有任務(wù)按指定順序來(lái)執(zhí)行炮姨,不需要處理線(xiàn)程同步的問(wèn)題。
例子:
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int finalI = i;
newSingleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
Log.d(TAG, "線(xiàn)程名字:" + Thread.currentThread().getName() + " i=" + finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
打印結(jié)果:
由打印結(jié)果可知碰煌,10個(gè)任務(wù)始終在1個(gè)線(xiàn)程中執(zhí)行舒岸。
-
CachedThreadPool:線(xiàn)程數(shù)量不固定的線(xiàn)程池,它只有非核心線(xiàn)程芦圾。在執(zhí)行新的任務(wù)時(shí)吁津,當(dāng)線(xiàn)程池中有之前創(chuàng)建的可用線(xiàn)程就重用之前的線(xiàn)程,否則就新建一條線(xiàn)程堕扶。如果線(xiàn)程池中的線(xiàn)程在60秒未被使用就會(huì)被回收碍脏,這種線(xiàn)程池適合執(zhí)行大量的耗時(shí)較少的任務(wù)。
例子:
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int finalI = i;
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, "線(xiàn)程名字:" + Thread.currentThread().getName() + " i=" + finalI);
}
});
}
由打印結(jié)果可知稍算,線(xiàn)程1出現(xiàn)了很多次典尾,說(shuō)明有重用之前創(chuàng)建的線(xiàn)程。
-
ScheduledThreadPool:核心線(xiàn)程數(shù)量固定糊探,非核心線(xiàn)程數(shù)量不固定的線(xiàn)程池钾埂,非核心線(xiàn)程閑置時(shí)會(huì)被立即回收,這種線(xiàn)程池主要用于執(zhí)行定時(shí)任務(wù)和具有固定周期的重復(fù)任務(wù)科平。
例子:
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(3);
/**
* 延遲2秒執(zhí)行任務(wù)
*/
newScheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
Log.d(TAG, "線(xiàn)程名字:" + Thread.currentThread().getName() + "定時(shí)任務(wù)");
}
}, 2, TimeUnit.SECONDS);
/**
* 延遲1秒后每2秒執(zhí)行一次任務(wù)
*/
newScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.d(TAG, "線(xiàn)程名字:" + Thread.currentThread().getName() + "周期性任務(wù)");
}
}, 1, 2, TimeUnit.SECONDS);
由打印結(jié)果可知褥紫,定時(shí)任務(wù)是2秒后執(zhí)行任務(wù),周期性任務(wù)是延遲1秒后每2秒執(zhí)行一次任務(wù)瞪慧。
5.6 利用線(xiàn)程池實(shí)現(xiàn)多線(xiàn)程并發(fā)
前面的例子雖然解決了多線(xiàn)程操作同一數(shù)據(jù)出現(xiàn)線(xiàn)程安全的問(wèn)題髓考,但是并發(fā)卻沒(méi)了,這里利用多線(xiàn)程可以很好的解決弃酌。
代碼如下:
public class MainActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
//圖片數(shù)量
private int mImgNum;
//開(kāi)啟3條線(xiàn)程上傳圖片
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void multiThread(View view) {
mImgNum = 9;
MyRunnable myRunnable = new MyRunnable();
for (int i = 0; i < 9; i++) {
//提交任務(wù)
newFixedThreadPool.submit(myRunnable);
}
}
public class MyRunnable implements Runnable {
@Override
public void run() {
try {
//模擬上傳一張圖片需要1秒鐘的時(shí)間
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//加上synchronized進(jìn)行同步氨菇,保證在同一時(shí)刻只能有一個(gè)線(xiàn)程能夠訪問(wèn)
synchronized (MyRunnable.class) {
mImgNum--;
Log.d(TAG, Thread.currentThread().getName() + "上傳了一張圖片...,還剩" + mImgNum + "張圖片未上傳");
if (mImgNum == 0) {
Log.d(TAG, Thread.currentThread().getName() + "全部上傳成功");
}
}
}
}
}
打印結(jié)果如下:
由圖可知妓湘,每秒中上傳了3張圖片查蓉,確實(shí)實(shí)現(xiàn)了并發(fā),而且線(xiàn)程安全問(wèn)題也解決了榜贴。
相關(guān)源碼:多線(xiàn)程并發(fā) demo