帶你通俗易懂的理解——線(xiàn)程、多線(xiàn)程與線(xiàn)程池

進(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é)果如下:

image

由圖可知符欠,圖片數(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)
  1. 可以重用線(xiàn)程池中的線(xiàn)程溜畅,避免因?yàn)榫€(xiàn)程的創(chuàng)建和銷(xiāo)毀所帶來(lái)的性能開(kāi)銷(xiāo)捏卓。
  2. 能有效控制線(xiàn)程池的最大并發(fā)數(shù),避免大量的線(xiàn)程之間因互相搶占系統(tǒng)資源而導(dǎo)致的阻塞現(xiàn)象慈格。
  3. 能夠?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é)果如下:

image

由圖可知妓湘,每秒中上傳了3張圖片查蓉,確實(shí)實(shí)現(xiàn)了并發(fā),而且線(xiàn)程安全問(wèn)題也解決了榜贴。

相關(guān)源碼:多線(xiàn)程并發(fā) demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末豌研,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子唬党,更是在濱河造成了極大的恐慌鹃共,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件初嘹,死亡現(xiàn)場(chǎng)離奇詭異及汉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)屯烦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)坷随,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)房铭,“玉大人,你說(shuō)我怎么就攤上這事温眉「追耍” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵类溢,是天一觀的道長(zhǎng)凌蔬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)闯冷,這世上最難降的妖魔是什么砂心? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮蛇耀,結(jié)果婚禮上辩诞,老公的妹妹穿的比我還像新娘。我一直安慰自己纺涤,他們只是感情好译暂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著撩炊,像睡著了一般外永。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拧咳,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天伯顶,我揣著相機(jī)與錄音,去河邊找鬼呛踊。 笑死砾淌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谭网。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼赃春,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼愉择!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起织中,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锥涕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后狭吼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體层坠,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年刁笙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了破花。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谦趣。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖座每,靈堂內(nèi)的尸體忽然破棺而出前鹅,到底是詐尸還是另有隱情,我是刑警寧澤峭梳,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布舰绘,位于F島的核電站,受9級(jí)特大地震影響葱椭,放射性物質(zhì)發(fā)生泄漏捂寿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一孵运、第九天 我趴在偏房一處隱蔽的房頂上張望者蠕。 院中可真熱鬧,春花似錦掐松、人聲如沸踱侣。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)抡句。三九已至,卻和暖如春杠愧,著一層夾襖步出監(jiān)牢的瞬間待榔,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工流济, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锐锣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓绳瘟,卻偏偏與公主長(zhǎng)得像雕憔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子糖声,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容