Java多線程開發之------多線程等待

我們在開發過程中,會有這樣的場景:

我有兩個線程,一個線程去獲取一個網絡數據,我要等待兩個線程回來之後,結合他們的數據去展示到ui裏面。怎麼實現:

面試被問到這個問題,我的回答是:

設置兩個boolean 變量,一個線程OK 之後,就把這個線程的變量置爲true,兩個都OK 了就更新Ui.

面試官說這是最基本的,有沒有更好的方法。
CountDownLatch

方案一:使用CountDownLatch

看下這個類的說明:

 * A synchronization aid that allows one or more threads to wait until
 * a set of operations being performed in other threads completes.

意思是,這個類可以允許我們讓一個或者多個線程,等待其他線程的一系列操作。

什麼意思,就是這個類可以幫助我們實現,上面提到的功能。我們可以利用這個類讓線程進入等待狀態,當兩個線程都結束了任務,那麼喚醒線程去做一些事情!

    /**
     *  線程等待鎖 測試
     */
    public void testWaitForAll(){
        CountDownLatch countDownLatch = new CountDownLatch(2);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {

                    Thread.sleep(1000);

                    countDownLatch.countDown();
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //toast 不能在子線程彈 爲什麼?

                            Toast.makeText(getActivity(), "thead1 down", Toast.LENGTH_SHORT).show();
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    countDownLatch.countDown();

                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //toast 不能在子線程彈 爲什麼?

                            Toast.makeText(getActivity(), "thead2 down", Toast.LENGTH_SHORT).show();
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });



        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    countDownLatch.await();
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //toast 不能在子線程彈 爲什麼?
                            Toast.makeText(getActivity(), "all work is work", Toast.LENGTH_SHORT).show();
                        }
                    });

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        executorService.shutdown();
    }


CountDownLatch countDownLatch = new CountDownLatch(2);

構造方法裏面需要穿一個參數,表示我需要等待幾個鎖。

然後我們調用countDownLatch.await();他就會讓當前的線程等待,等兩個任務都執行完了之後,就會喚醒當前線程。

當一個線程完成之後,調用countDownLatch.countDown(); 就表示我已經完成了一個線程的任務,當所有的任務都執行,就會喚醒等待的那個線程。

上面的CountDownLatch 只能使用一次,不能重複使用,如果要重複使用,那麼使用下面的:

方案二: 使用CyclicBarrier

    /**
     * CyclicBarrier  每一個線程 都要等待  而 CountDownLatch 不是
     */
    public void testCyclicBarrier(){
        CyclicBarrier countDownLatch = new CyclicBarrier(2, new Runnable() {
            @Override
            public void run() {
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //toast 不能在子線程彈 爲什麼?
                        Toast.makeText(getActivity(), "all work is work", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {

                    Thread.sleep(1000);
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //toast 不能在子線程彈 爲什麼?

                            Toast.makeText(getActivity(), "thead1 down", Toast.LENGTH_SHORT).show();
                        }
                    });
                    try {
                        countDownLatch.await();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //toast 不能在子線程彈 爲什麼?

                            Toast.makeText(getActivity(), "thead2 down", Toast.LENGTH_SHORT).show();
                        }
                    });
                    try {
                        countDownLatch.await();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });



    }

CyclicBarrier 構造參數裏面有兩個,第一個表示我要等待幾個鎖,第二個表示當所有的任務都執行完之後,執行的一個Runnable.
如果有線程執行完成,那麼就調用await 方法。每調用一個await,CyclicBarrier 裏面當前未執行的任務數量就會減少1.
當時有一個缺點就是所有的線程都會一起等待。

如果需要重複使用CyclicBarrier 就countDownLatch.reset();就可以了。

方案三:使用Future

        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future<?> futureOne = executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {

                    Thread.sleep(1000);
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //toast 不能在子線程彈 爲什麼?

                            Toast.makeText(getActivity(), "thead1 down", Toast.LENGTH_SHORT).show();
                        }
                    });

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Future<?> futureTwo = executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //toast 不能在子線程彈 爲什麼?

                            Toast.makeText(getActivity(), "thead2 down", Toast.LENGTH_SHORT).show();
                        }
                    });

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    futureOne.get();
                    futureTwo.get();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //toast 不能在子線程彈 爲什麼?
                        Toast.makeText(getActivity(), "all work is work", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });


Future get 方法會阻塞等待線程中任務的執行完成。

    /**
     * Waits if necessary for the computation to complete, and then
     * retrieves its result.
     *
     */
    V get() throws InterruptedException, ExecutionException;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章