1. 程式人生 > >Android面試一天一題(16 Day: 執行緒同步)

Android面試一天一題(16 Day: 執行緒同步)

最近領導讓我在外掛框架上加上一個介面,在宿主應用中可以呼叫所有的外掛去清除自己的快取資料,當完成所有外掛的清除任務後再執行下一步操作。領導就是需求嘛,領導改變注意那是再正常不過的事了,但是不是領導的需求人員有可能變多了會有人身危險。我平時和同事吹牛時,常和他們說工作的七字真言:

“不急、不怕、不要臉”(抄自馮唐)

我認為對於軟體開發來說,這句話很值得品味。在需求改變時,不要急於修改程式碼,而是要先做一個全盤的考慮,有些時候你還沒考慮好,需求方就說不要做了。在接到新任務或者遇到困難時不要怕,沒什麼可怕的,你不難受你就不會有提高。最後,不要臉更是程式設計師最需要的,這個自己體會。

面試題:如何處理執行緒同步的問題?

有可能很多人對外掛並不瞭解,不過沒關係,這個需求簡單地說就是主執行緒要等待多個子執行緒全部完成工作後,才能繼續執行。

說到多執行緒的同步問題,面試多的人應該很容易被面試官問:Object的wait和notify/notifyAll如何實現執行緒同步?

在Object.java中,定義了wait(), notify()和notifyAll()等介面。wait()的作用是讓當前執行緒進入等待狀態,同時,wait()也會讓當前執行緒釋放它所持有的鎖。而notify()和notifyAll()的作用,則是喚醒當前物件上的等待執行緒;notify()是喚醒單個執行緒,而notifyAll()是喚醒所有的執行緒。

wait和yield(或sleep)的區別?

  1. wait()是讓執行緒由“執行狀態”進入到“等待(阻塞)狀態”,而yield()是讓執行緒由“執行狀態”進入到“就緒狀態”,從而讓其它具有相同優先順序的等待執行緒獲取執行權;但是,並不能保證在當前執行緒呼叫yield()之後,其它具有相同優先順序的執行緒就一定能獲得執行權。
  2. wait()是會執行緒釋放它所持有物件的同步鎖,而yield()方法不會釋放鎖。

而我接觸到的很多情況是:問執行緒同步的問題,大多數人基本上只知道synchronized。

要搞清執行緒的同步問題,大家要先了解一下“物件的同步鎖”,這個留給大家自己去看吧,這裡不做展開。我們回到新接到的這個需求上來,這個場景其實挺合適做為一個面試題的。

如何實現呢?我想到一個簡單的方法就是用CountDownLatch。

CountDownLatch:一個同步輔助類(大名鼎鼎的java.util.concurrent包),在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。

用給定的任務數初始化CountDownLatch,一個執行緒工作完成(任務成功或者失敗都算工作完成)就呼叫 countDown() 方法,當計數到達零之前,await 方法會一直受阻塞。當計數器為零時,會釋放所有等待的執行緒,await後的程式碼將被執行。

CountDownLatch計數無法被重置。如果需要重置計數,請考慮使用 CyclicBarrier。

還有其他的實現方式嗎?這個是肯定的。比如,真接上Thread.jion,程式碼難看是會難看點,但也能完成這個需求。

我還查到一種方式是使用java.util.concurrent.ExecutorService的awaitTermination阻塞主執行緒,等待執行緒池的所有執行緒執行完成。需要設定一個超時時間的引數,如果超時則awaitTermination返回false,如果執行緒池中的執行緒全部執行完成,返回true。

小結

因為現在有很多開源的框架或者程式碼庫,幫我們解決了很多底層諸如網路請求、執行緒池管理的問題,使得很多情況下我們都不怎麼接觸到執行緒同步的問題。不過還有很有必要抽時間來學習一些執行緒同步的知識,對我們提高併發程式設計的能力很有幫助。

如果大家有更好的方式實現我開頭提到的需求,可以回帖一起討論一下。