Android中的線程問題

本文將一步步的探索Android中線程相關(guān)的知識搏屑,思路線索:單線程 - > 多線程 - > 線程池 - > 線程間通訊 - > 線程鎖稿存。

對應(yīng)出現(xiàn)類的順序
Thread -> Runnable -> Callable -> Future -> FutureTask -> Executor -> ExecutorService -> AbstractExecutorService -> ThreadPoolExecutor -> Executors -> AsyncTask -> Handler -> Looper -> MessageQueue -> Message -> Object -> Lock -> ReadWriteLock -> Condition -> Semaphore -> CountDownLatch

單線程

Thread、Runnable

使用多線程時,耗時操作放到run方法內(nèi)册倒,這樣就簡單的創(chuàng)建了一個子線程。

        //方式一
        new Thread() {
            @Override
            public void run() {
                //do some thing
            }
        }.start();
        //方式二
        new Thread(new Runnable() {
            @Override
            public void run() {
                //do some thing
            }
        }).start();

這兩種方式有什么區(qū)別嗎磺送?

看看Thread源碼

Thread自身實(shí)現(xiàn)了Runnable,而Runnable是一個接口灿意,Thread的run方法做了什么呢估灿?

//Thread源碼
    /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {//此處的target為構(gòu)造函數(shù)傳入的Runnable對象
            target.run();
        }
    }

方式一重寫了此run方法,方法二則為target賦值缤剧。

thread對象通過start()方法將自身傳遞到native層開啟了一個線程馅袁,最終調(diào)用thread對象的run方法。

如果使用thread.run()荒辕,則會在創(chuàng)建thread對象的線程內(nèi)執(zhí)行run()汗销,并沒有創(chuàng)建新的線程。

使用Thread+ Runnable的這種方法有個弊端抵窒,比如:當(dāng)我數(shù)據(jù)庫插入操作的時候弛针,我想知道數(shù)據(jù)最后到底插入成功了沒有我是無法被通知到的。

Runnable是個啞巴李皇,就只會干活削茁,干好干壞干完沒有它是不會告訴你的。

Callable

Callable是在JDK1.5時才有的掉房,它解決了任務(wù)完成后沒有返回值的問題

public interface Callable<V> {
    V call() throws Exception;
}

源碼中包含的泛型即返回值類型茧跋,但使用的時候callable.call(),由于在子線程內(nèi)執(zhí)行耗時操作卓囚,并不能即時的得到響應(yīng)返回數(shù)據(jù)瘾杭,因此會造成阻塞。

如果這時能夠知道任務(wù)結(jié)束了哪亿,就方便很多粥烁。當(dāng)然源碼做了!锣夹!

Future

同Callable也是在JDK1.5時才有的

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);//取消當(dāng)前任務(wù)页徐,分幾種情況:
    //當(dāng)前任務(wù)等待 - 無視傳入?yún)?shù) 取消任務(wù)
    //當(dāng)前任務(wù)執(zhí)行 - mayInterruptIfRunning=true 中斷任務(wù)
    //當(dāng)前任務(wù)執(zhí)行 - mayInterruptIfRunning=false 不會中斷任務(wù)
    //當(dāng)前任務(wù)完成此方法無效

    boolean isCancelled();//當(dāng)前任務(wù)是否被取消

    boolean isDone();//當(dāng)前任務(wù)是否完成

    V get() throws InterruptedException, ExecutionException;//獲取任務(wù)的結(jié)果,會產(chǎn)生阻塞

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;//獲取任務(wù)的結(jié)果有超時時間
}

這個接口是用來對任務(wù)進(jìn)行操作的银萍。

有了能夠返回任務(wù)完成結(jié)果的Callable变勇,有了操作任務(wù)的Future,怎么結(jié)合使用呢?

FutureTask

FutureTask是集Runnable搀绣、Callable涂臣、Future一起使用的類,F(xiàn)utureTask實(shí)現(xiàn)了Runnable的run方法甫题,將傳入的Callable對象在此方法進(jìn)行執(zhí)行晴叨,同時將任務(wù)的返回結(jié)果通過done()方法回調(diào)。FutureTask同時實(shí)現(xiàn)了Future接口麻捻,用于控制任務(wù)的執(zhí)行過程纲仍,由于所有工作都在在子線程執(zhí)行所以不會對主線程造成阻塞。

FutureTask的構(gòu)造函數(shù)

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);//將Runnable轉(zhuǎn)為Callable
        this.state = NEW;       // ensure visibility of callable
    }

FutureTask的run

    public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();//獲取Callable的返回值贸毕,此處阻塞
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);//保存結(jié)果
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

FutureTask的結(jié)果

    protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            U.putOrderedInt(this, STATE, NORMAL); // final state
            finishCompletion();
        }
    }

    private void finishCompletion() {
              //忽略代碼
             done();//回調(diào)郑叠,通知結(jié)束
    }

FutureTask的邏輯了解了,具體怎么使用呢明棍?乡革?

因為FutureTask實(shí)現(xiàn)了Runnable接口,所以用法類似Runnable摊腋。

        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 1;
            }
        };

        FutureTask<Integer> futureTask = new FutureTask<Integer>(callable) {
            @Override
            protected void done() {
                try {
                    get();//任務(wù)完成回調(diào)方法沸版,獲取任務(wù)返回結(jié)果會拋異常
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        new Thread(futureTask).start();

多線程

一份工作合理地分給多個人干,比全部交給一個人干兴蒸,完成的時間肯定要少视粮,這里的多個人即為多線程。

一份工作開始1個人做橙凳,后來為了趕時間招了1個共2個人做馒铃,發(fā)現(xiàn)時間還是不夠又要招1個。做這份工作的時候除了實(shí)際的工作時間痕惋,花費(fèi)最多的應(yīng)該就是招人的時間了区宇。
這時有了勞務(wù)派遣公司,我要人的時候立刻就會送人過來值戳,這樣就不用花時間去招人议谷。

線程也是一樣,由于線程的創(chuàng)建與銷毀都需要耗費(fèi)資源堕虹,所以多線程都會用到線程池卧晓,當(dāng)工作需要線程的時候就去線程池中取。

線程池

常規(guī)的幾種

  • FixedThreadPool 固定線程數(shù)量的線程池
  • SingleThreadExecutor 單個線程數(shù)量的線程池
  • CachedThreadPool 可緩存線程的線程池
  • SingleThreadScheduledExecutor 單線程計劃線程池
  • ScheduledThreadPool 計劃線程池

非常規(guī)(待細(xì)分析)

  • WorkStealingPool(工作竊取模式赴捞,一個任務(wù)太大可以fork成多個任務(wù)逼裆,每個小任務(wù)執(zhí)行完成后join形成最后的結(jié)果)

它們是通過策略模式(接口Executor、ExecutorService赦政、AbstractExecutorService:策略胜宇,類ThreadPoolExecutor:各種策略的實(shí)現(xiàn)耀怜,策略的切換類:Executors)產(chǎn)生的。策略模式是一種通過接口統(tǒng)一行為桐愉,繼承接口的類來實(shí)現(xiàn)具體的操作财破,通常會有一個策略的切換類用以更換策略,是一種創(chuàng)建型的設(shè)計模式从诲。

通過源碼追蹤看到Executors只是創(chuàng)建線程池的一個工具類左痢,內(nèi)除了靜態(tài)方法就是一個

所以此類不能夠?qū)嵗?/p>

除ForkJoinPool外其余線程池都是ThreadPoolExecutor實(shí)例化出來的。

ThreadPoolExecutor

關(guān)鍵的構(gòu)造函數(shù)

    public ThreadPoolExecutor(int corePoolSize,//核心線程數(shù)
                              int maximumPoolSize,//池內(nèi)最大線程數(shù)
                              long keepAliveTime,//線程的存活時間
                              TimeUnit unit,//時間單位
                              BlockingQueue<Runnable> workQueue,//線程排列順序
                              ThreadFactory threadFactory,//線程生成器
                              RejectedExecutionHandler handler) //當(dāng)線程數(shù)超出限制時的處理

由ThreadPoolExecutor實(shí)例化出來的線程池即勞務(wù)派遣公司系洛,內(nèi)部有人員配置指標(biāo)俊性,合同工(核心線程)為corePoolSize人,工人總數(shù)(總線程數(shù))maximumPoolSize描扯,所有人員均來自threadFactory磅废。

公司之初為了減少人工成本,沒有工人荆烈,來一個任務(wù)簽一個合同工,此時即使有合同工閑著也要招一個合同工竟趾,直到達(dá)到指標(biāo)憔购。

合同工的合同期限為永久,當(dāng)合同工全部在忙又有新任務(wù)時岔帽,還要招些臨時工(一般線程)玫鸟,要符合人員配置指標(biāo)。但臨時工的合同時間很短為keepAliveTime單位為unit犀勒。

當(dāng)有人需要勞務(wù)派遣時屎飘,優(yōu)先合同工,其次才是臨時工贾费,任務(wù)多到現(xiàn)有maximumPoolSize人數(shù)做不完時钦购,先將任務(wù)保存到workQueue等待處理,任務(wù)繼續(xù)增加到workQueue存不下了handler就會來處理褂萧。

淡季到來時由于旺季人員全招滿了有maximumPoolSize之多押桃,所以輪到與臨時工解除合同了,凡到期keepAliveTime的臨時工將離開勞務(wù)派遣公司导犹,只剩合同工唱凯。

這就是勞務(wù)派遣公司的運(yùn)營模式,但不同地區(qū)情況不同谎痢,不能按這種模式去適應(yīng)所有情況磕昼,所以在此運(yùn)營模式的基礎(chǔ)上,各地有自己特色的勞務(wù)派遣公司

FixedThreadPool

固定線程數(shù)量的線程池

核心線程數(shù)即總線程數(shù)节猿,keepAliveTime=0票从。此勞務(wù)派遣公司不招臨時工,工作全部由合同工做。

SingleThreadExecutor

單個線程數(shù)量的線程池纫骑,其他線程池后綴為pool蝎亚,但它卻是executor也表示出了單數(shù)的意思。

此勞務(wù)派遣公司不僅不招臨時工先馆,而且就只有一個合同工发框,且工作全部由他做。

CachedThreadPool

可緩存線程的線程池

此勞務(wù)派遣公司沒有合同工全部是臨時工煤墙,且臨時工數(shù)量非常大Integer.MAX_VALUE梅惯。

多線程的使用

//Callable
        Executors.newSingleThreadExecutor().submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 0;
            }
        });
//Runnable
        Executors.newSingleThreadExecutor().submit(new Runnable() {
            @Override
            public void run() {
                
            }
        });
//FutureTask
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 0;
            }
        };

        Executors.newSingleThreadExecutor().submit(new FutureTask<Integer>(callable) {
            @Override
            protected void done() {
                //任務(wù)結(jié)束可以取結(jié)果了
            }
        });

通過Executors.newSingleThreadExecutor()獲取適合的線程池,將FutureTask任務(wù)submit到線程池仿野,當(dāng)任務(wù)完成回調(diào)done方法铣减,如果我們在done方法內(nèi)再起一個線程或回到主線程,不就實(shí)現(xiàn)了線程間到通訊了嗎脚作?葫哗?

線程間通訊

根據(jù)上面的思路,ThreadPool執(zhí)行完FutureTask會回調(diào)done方法球涛,在done方法內(nèi)任務(wù)的執(zhí)行已經(jīng)有了結(jié)果劣针,此時回到主線程就形成了真正意義上的后臺執(zhí)行效果,官方提供的AsyncTask就是此思路亿扁。

AsyncTask

由于存在版本差異捺典,這里只是線程間通訊的大體思路,源碼來自API26

AsyncTask的使用

        AsyncTask<String, Integer, Long> asyncTask = new AsyncTask<String, Integer, Long>() {//三個泛型分別為:參數(shù)類型从祝、進(jìn)度返回類型襟己、結(jié)果類型
            @Override
            protected void onPreExecute() {//主線程
                super.onPreExecute();
            }

            @Override
            protected Long doInBackground(String... strings) {//子線程
                publishProgress(10);//通知處理過程,回調(diào)到主線程的onProgressUpdate
                return null;
            }

            @Override
            protected void onProgressUpdate(Integer... values) {//主線程
                super.onProgressUpdate(values);//接收處理過程
            }

            @Override
            protected void onPostExecute(Long aLong) {//主線程
                super.onPostExecute(aLong);
            }
        };

        asyncTask.execute("");//開始執(zhí)行

內(nèi)部線程間通訊邏輯

//AsyncTask的構(gòu)造函數(shù)

    public AsyncTask() {
        this((Looper) null);
    }

    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }

    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);//Handler牍陌、Looper后面介紹擎浴,通過它回mHandler所在的線程,此處理解為主線程即可

        mWorker = new WorkerRunnable<Params, Result>() {//實(shí)現(xiàn)Callable的類
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);//調(diào)用doInBackground方法毒涧,即自己重寫的耗時操作
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);//返回結(jié)果
                }
                return result;//返回結(jié)果
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {//任務(wù)對象
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());//調(diào)用postResult返回結(jié)果
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
//任務(wù)執(zhí)行
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);//第一個參數(shù)為默認(rèn)的線程池
    }

    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;//修改任務(wù)狀態(tài)

        onPreExecute();//調(diào)用onPreExecute方法退客,即自己重寫的準(zhǔn)備工作方法

        mWorker.mParams = params;
        exec.execute(mFuture);//將任務(wù)丟給線程池,進(jìn)行異步執(zhí)行

        return this;
    }
//將結(jié)果給Handler
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
//內(nèi)部的Handler
    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);////調(diào)用onPostExecute方法链嘀,即自己重寫的獲取結(jié)果方法
        }
        mStatus = Status.FINISHED;
    }
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

總結(jié):
1萌狂、創(chuàng)建FutureTask
2、交給線程池處理
3怀泊、回調(diào)done時茫藏,將任務(wù)執(zhí)行結(jié)果組裝成Message傳給Handler
4、Handler在主線程處理

為什么Handler的處理就是在主線程了呢霹琼?

Handler务傲、Looper凉当、Message、MessageQueue

Handler

構(gòu)造函數(shù)


主要構(gòu)造函數(shù)

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();//獲取當(dāng)前線程的Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//從Looper里拿出消息隊列
        mCallback = callback;
        mAsynchronous = async;
    }

使用

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//注意這里的target
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

到此Message被存到MessageQueue中售葡,MessageQueue又是在Looper中取得的看杭。

Looper

Looper的構(gòu)造函數(shù)為private,因此外部不能new出實(shí)例挟伙。那Looper怎么來的楼雹,此處不深入直接跳出答案。

在Android中所有進(jìn)程的入口如Java一樣是main函數(shù)尖阔,此函數(shù)在ActivityThread

圖中看出Looper的實(shí)例化贮缅。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//當(dāng)前線程

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();//取出當(dāng)前線程的Looper設(shè)為主Looper
        }
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//為當(dāng)前線程設(shè)置Looper
    }

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

main方法里還有個Looper.loop()開啟循環(huán)

    public static void loop() {
        final Looper me = myLooper();//拿到Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//拿到Looper的MessageQueue

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {//無限循環(huán)
            Message msg = queue.next(); // might block在MessageQueue中取出Message
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);//Message被送往target,前面Handler在送Message入MessageQueue時有提醒介却,此處的target就是Handler
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

到此知道Looper在主線程不停的從MessageQueue中取出由Handler送來的Message去Handler處理谴供。

總結(jié):MessageQueue只是個暫存處,是Looper的成員變量齿坷。每一個線程有且僅有一個Looper桂肌,意味著有且僅有一個MessageQueue。Handler創(chuàng)建的時候指定了Looper永淌,當(dāng)多個Handler對象sendMessage時崎场,message其實(shí)都去了同一個MessageQueue,然后到時間Looper又將其取出來送回原Handler仰禀。
線程間通訊時,將主線程創(chuàng)建的Handler對象作為變量傳遞到子線程蚕愤,其發(fā)出的Message又會回到主線程的Handler進(jìn)行處理答恶,因此實(shí)現(xiàn)來線程間的通訊。

    public void dispatchMessage(Message msg) {//由Looper送回的Message
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//為重寫方法
        }
    }

MessageQueue

是如何存儲Message的呢萍诱?

在Looper循環(huán)的時候悬嗓,看到Message msg = queue.next()。沒錯裕坊!MessageQueue就是個單鏈表包竹。

到此android的線程間通訊就告一段落了,android主要以消息隊列的方式進(jìn)行線程間通訊籍凝,runOnUiThread也是Handler做的周瞎,當(dāng)然通訊的方式還有管道、共享內(nèi)存等饵蒂。

多線程操作帶來的不僅僅是效率声诸,控制不好也會出現(xiàn)問題。
a=10
線程一取a=10加5得15
線程二取a=10減5得5存a=5
此時線程一才反應(yīng)過來存a=15
那具體的操作到底誰先誰后退盯,a最后等于幾彼乌?

線程鎖

synchronized關(guān)鍵字

synchronized應(yīng)該是最常見的控制線程同步問題的解決方法泻肯,通過獲取指定Object的鎖來達(dá)到同步的目的。

public class MyClass  {
    
    public void method1(Object object){//獲取由MyClass產(chǎn)生對象的鎖
        synchronized (this){
            //todo
        }
    }

    public void method2(Object object){//獲取object對象的鎖
        synchronized (object){
            //todo
        }
    }

    public void method3(Object object){//獲取MyClass這個類對象的鎖
        synchronized (MyClass.class){
            //todo
        }
    }

    public synchronized void method4(Object object){//獲取由MyClass產(chǎn)生對象的鎖
        //todo
    }

    public static synchronized void method5(Object object){//獲取MyClass這個類對象的鎖
        //todo
    }

}

Object

android中所有的類的基類慰照,synchronized也是通過Object才可以給類對象和對象加鎖灶挟,可以看到相關(guān)的方法是native層的。

wait()毒租、notify()稚铣、notifyAll()
這三個方法是 java.lang.Object 的 final native 方法,任何繼承 java.lang.Object 的類都有這三個方法蝌衔。它們是Java語言提供的實(shí)現(xiàn)線程間阻塞和控制進(jìn)程內(nèi)調(diào)度的底層機(jī)制榛泛,平時我們會很少用到的。

wait():
導(dǎo)致線程進(jìn)入等待狀態(tài)噩斟,直到它被其他線程通過notify()或者notifyAll喚醒曹锨,該方法只能在同步方法中調(diào)用。

notify():
隨機(jī)選擇一個在該對象上調(diào)用wait方法的線程剃允,解除其阻塞狀態(tài)沛简,該方法只能在同步方法或同步塊內(nèi)部調(diào)用。

notifyAll():
解除所有那些在該對象上調(diào)用wait方法的線程的阻塞狀態(tài)斥废,同樣該方法只能在同步方法或同步塊內(nèi)部調(diào)用椒楣。

調(diào)用這三個方法中任意一個,當(dāng)前線程必須是鎖的持有者牡肉,如果不是會拋出一個 IllegalMonitorStateException 異常捧灰。

wait()釋放鎖 Thread.sleep(long time)不釋放鎖

Lock

Lock本身是一個接口,真正的實(shí)現(xiàn)類是ReentrankLock统锤。它的使用與synchronized類似毛俏,獲取ReentrankLock對象的鎖來實(shí)現(xiàn)線程的同步,但多了鎖的一些操作饲窿。synchronized為native層控制而Lock為代碼控制煌寇,所以加鎖與解鎖必須成對出現(xiàn)。簡單場景synchronized效率高逾雄,到了多線程復(fù)雜操作時ReentrankLock穩(wěn)定到效率得以展現(xiàn)阀溶。

這里有個公平的問題,鎖分為公平鎖與不公平鎖鸦泳,默認(rèn)構(gòu)造非公平鎖银锻。此處的公平并不是優(yōu)先級,而是線程來的時間先后做鹰。公平鎖按線程FIFO的順序處理徒仓,非公平鎖則隨機(jī)從線程隊列中取,若剛好釋放鎖的一刻來了個線程則此線程插隊了線程隊列里正在等待的其他線程誊垢。

    public void method(){
        ReentrantLock lock=new ReentrantLock();

        try{
            lock.lock();
            //todo
        }finally {
            lock.unlock();//切記釋放鎖
        }
        
    }

ReadWriteLock

上面的鎖好用掉弛,但某些場景卻效率低下症见。

一本書
線程一加鎖:讀
線程二來等待
線程一讀完解鎖
線程二加鎖:讀

這種場景下書的內(nèi)容沒有變化,完全可以線程一二共同讀不必讓線程二消耗時間等待殃饿,因此就有了ReadWriteLock它的實(shí)現(xiàn)類是ReentrantReadWriteLock谋作。

其內(nèi)部也是實(shí)現(xiàn)了Lock接口,針對讀寫的特性內(nèi)部控制了是否放行線程乎芳。

Condition

用來代替Object的

public interface Condition {

    //讓當(dāng)前線程等待遵蚜,直到線程發(fā)出信號signal或被線程中斷
    void await() throws InterruptedException;

    //讓當(dāng)前線程等待,直到線程發(fā)出信號signal
    void awaitUninterruptibly();

    //讓當(dāng)前線程等待奈惑,直到線程發(fā)出信號signal吭净、被線程中斷或等超時
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    //讓當(dāng)前線程等待,直到線程發(fā)出信號signal肴甸、被線程中斷或等超時
    boolean await(long time, TimeUnit unit) throws InterruptedException;

    //讓當(dāng)前線程等待寂殉,直到線程發(fā)出信號signal、被線程中斷或到終止時間
    boolean awaitUntil(Date deadline) throws InterruptedException;

    //喚醒一個等待線程
    void signal();

    //喚醒所有等待線程
    void signalAll();
}

看個例子來自https://www.2cto.com/kf/201609/549780.html

/*
 * 生產(chǎn)者與消費(fèi)者問題
 */
public class ProduceConsume {
    public static void main(String[] args) {
        SyncStack ss = new SyncStack();
        Produce pd = new Produce(ss);
        Consume cs = new Consume(ss);
        Thread t1 = new Thread(pd);
        Thread t2 = new Thread(cs);
        t1.start();
        t2.start();
    }
}
 
/*
 * 饅頭實(shí)體類
 */
class ManTou {
    private int id;
 
    public ManTou(int id) {
        this.id = id;
    }
 
    public int getId() {
        return this.id;
    }
 
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "ManTou " + getId();
    }
}
 
/*
 * 饅頭框類
 */
class SyncStack {
    Lock lock = new ReentrantLock();
    Condition full = lock.newCondition();
    Condition empty = lock.newCondition();
    int index = 0;
    ManTou[] mtArray = new ManTou[6];
 
    public void push(ManTou mt) {
        lock.lock();
        try {
            while (index == mtArray.length) {
                try {
                    full.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            empty.signal();
            mtArray[index] = mt;
            index++;
            System.out.println("生產(chǎn)了" + mt);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
 
    public void pop() {
        lock.lock();
        try {
            while (index == 0) {
                try {
                    empty.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            full.signal();
            index--;
            System.out.println("消費(fèi)了" + mtArray[index]); 
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
 
/*
 * 生產(chǎn)者
 */
class Produce implements Runnable {
    SyncStack ss = null;
 
    public Produce(SyncStack ss) {
        this.ss = ss;
    }
 
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 20; i++) {
            ManTou mt = new ManTou(i);
            if (ss != null) {
                ss.push(mt);
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
}
 
/*
 * 消費(fèi)者
 */
class Consume implements Runnable {
    SyncStack ss = null;
 
    public Consume(SyncStack ss) {
        this.ss = ss;
    }
 
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 20; i++) {
            if (ss != null) {
                ss.pop();
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
}

Semaphore

上面這些鎖在某一時刻只能有一個線程進(jìn)入原在,實(shí)際場景可能并不適用友扰。

公共廁所共3個坑位
來1個人占1個
這時來了3個人全部占滿了
第4個人就在等著
直到有1個人離開,第4個人才進(jìn)入

    private Semaphore semaphore=new Semaphore(3);
    public void bathroom(SomeOne someOne){
        semaphore.acquire();
        
        someOne.doSomething();

        semaphore.release();

    }

CountDownLatch

學(xué)習(xí)最好的資料就是源碼庶柿,看看官方的例子:

//例子一
  class Driver { // ...
    void main() throws InterruptedException {
      CountDownLatch startSignal = new CountDownLatch(1);
      CountDownLatch doneSignal = new CountDownLatch(N);
 
      for (int i = 0; i < N; ++i) // create and start threads
        new Thread(new Worker(startSignal, doneSignal)).start();
 
      doSomethingElse();            // don't let run yet
      startSignal.countDown();      // let all threads proceed
      doSomethingElse();
      doneSignal.await();           // wait for all to finish    等doneSignal變0
    }
  }

  class Worker implements Runnable {
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;
    Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
      this.startSignal = startSignal;
      this.doneSignal = doneSignal;
    }
    public void run() {
      try {
        startSignal.await();//等待startSignal變?yōu)?
        doWork();
        doneSignal.countDown();//完成工作  計數(shù)
      } catch (InterruptedException ex) {} // return;
    }
 
    void doWork() { ... }
  }
//例子二
  class Driver2 { // ...
    void main() throws InterruptedException {
      CountDownLatch doneSignal = new CountDownLatch(N);
      Executor e = ...
 
      for (int i = 0; i < N; ++i) // create and start threads
        e.execute(new WorkerRunnable(doneSignal, i));
 
      doneSignal.await();           // wait for all to finish等doneSignal變?yōu)?
    }
  }

  class WorkerRunnable implements Runnable {
    private final CountDownLatch doneSignal;
    private final int i;
    WorkerRunnable(CountDownLatch doneSignal, int i) {
      this.doneSignal = doneSignal;
      this.i = i;
    }
    public void run() {
      try {
        doWork(i);
        doneSignal.countDown();//doneSignal計數(shù)減一
      } catch (InterruptedException ex) {} // return;
    }
 
    void doWork() { ... }
  }

看過一個問題:5個線程打印hello world村怪,先打印hello再打印world

    CountDownLatch countDownLatch=new CountDownLatch(5);
    
    class Worker implements Runnable{

        @Override
        public void run() {
            try {
                System.out.print("hello");
                countDownLatch.countDown();
                countDownLatch.await();
                System.out.print("world");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

線程中的其他問題

volatile關(guān)鍵字

作用:

  • 保證了不同線程對這個變量進(jìn)行操作時的可見性,即一個線程修改了某個變量的值浮庐,這新值對其他線程來說是立即可見的甚负。

  • 禁止進(jìn)行指令重排序(即不能將在對volatile變量訪問的語句放在其后面執(zhí)行,也不能把volatile變量后面的語句放到其前面執(zhí)行)审残。

join()方法

在Thread類中有join()方法梭域,它能夠?qū)⒉⑿懈臑榇小H绾尾僮鞯哪匚Γ空f白了就是插隊碰辅。

    public void method()  {
        Thread t1 = new Thread();
        Thread t2= new Thread();
        t1.start();//主線程啟動t1懂昂,當(dāng)前線程為主線程
       
        t1.join();//t1插隊主線程介时,主線程等待t1完成
        t2.start();//主線程拿回控制權(quán),開啟t2
    }

Thread.yield()方法

讓當(dāng)前線程從運(yùn)行態(tài)轉(zhuǎn)為就緒態(tài)凌彬,此時線程進(jìn)入等待隊列沸柔,如果此線程優(yōu)先級較高,則又會被調(diào)用铲敛,并不是Thread.yield()后就一定會將cpu交給其他線程褐澎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市伐蒋,隨后出現(xiàn)的幾起案子工三,更是在濱河造成了極大的恐慌迁酸,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俭正,死亡現(xiàn)場離奇詭異奸鬓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掸读,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門串远,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人儿惫,你說我怎么就攤上這事澡罚。” “怎么了肾请?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵留搔,是天一觀的道長。 經(jīng)常有香客問我筐喳,道長催式,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任避归,我火速辦了婚禮荣月,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘梳毙。我一直安慰自己哺窄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布账锹。 她就那樣靜靜地躺著萌业,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奸柬。 梳的紋絲不亂的頭發(fā)上生年,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機(jī)與錄音廓奕,去河邊找鬼抱婉。 笑死,一個胖子當(dāng)著我的面吹牛桌粉,可吹牛的內(nèi)容都是我干的蒸绩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼铃肯,長吁一口氣:“原來是場噩夢啊……” “哼患亿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起押逼,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤步藕,失蹤者是張志新(化名)和其女友劉穎惦界,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咙冗,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡表锻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乞娄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞬逊。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖仪或,靈堂內(nèi)的尸體忽然破棺而出确镊,到底是詐尸還是另有隱情,我是刑警寧澤范删,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布蕾域,位于F島的核電站,受9級特大地震影響到旦,放射性物質(zhì)發(fā)生泄漏旨巷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一添忘、第九天 我趴在偏房一處隱蔽的房頂上張望采呐。 院中可真熱鬧,春花似錦搁骑、人聲如沸斧吐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽煤率。三九已至,卻和暖如春乏冀,著一層夾襖步出監(jiān)牢的瞬間蝶糯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工辆沦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昼捍,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓众辨,卻偏偏與公主長得像端三,于是被迫代替她去往敵國和親舷礼。 傳聞我的和親對象是個殘疾皇子鹃彻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

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