標籤:android sleep wait
需求
最近項目中這麼一個功能,希望app監聽android file system,當有檔案create/modify/detele時,app收到通知,把對應的變化資訊告訴伺服器
需求的解決方案
當我們用手機拍照時,會把拍照的檔案儲存在sdcard的DCIM檔案夾下。當檔案產生時,我們會接收到create/modify/write_close訊息,我們要做的是在write_close之後,再發送訊息給伺服器,這樣一來,當我們收到create訊息時,就需要把當前線程阻塞住,直到收到write_close訊息之後再調用notifyAll來開啟阻塞的線程。
問題
問題出在:我們在接收到write_close訊息之後,雖然調用了setState(true),但是isChange的值並沒有馬上改變,而是過了好幾秒之後才被改變
來看看核心代碼
while (count > 0) { //阻塞線程的代碼,一下代碼簡稱:迴圈synchronized (this) {if (!isChange) { //當isChange不變為true時,一直等待,直到接收到write_close訊息,調用setState(true)try {Log.d(LOG_TAG, "sleeping ......");this.wait(1000); //這裡原先調用的是TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}continue;}}//...省略代碼sendMsg2Server();結束後break}
public synchronized void setState(boolean isChange){this.isChange = true;this.notifyAll();//原來是沒有加這句,在改成wait(1000)之後,調用notifyAll()}
問題解決方案
把TimeUnit.MILLISECONDS.sleep(1000)改成wait(1000),並且在setState(true)裡notifyAll()。
問題的根源
在if(!isChange)和setState()裡都用了同步塊,同步鎖是this。當我們調用sleep(1000)時,迴圈並沒有交出同步鎖,而是持有鎖直到1秒sleep完成,這時setState()和迴圈會競爭這個this鎖,setState()沒有競爭到這個鎖是很正常的,只要setState()沒有競爭到鎖,迴圈就會sleep 1秒,這樣就會造成列印了好幾句Log.d(LOG_TAG, "sleeping ......");之後才改變isChange的值的情況。
當我們把代碼改成wait(1000)之後,迴圈馬上把鎖交了出去,setState()拿到鎖,馬上改變isChange的值,並且通知迴圈,這樣就能進入後面sendMsg2Server()的代碼了
[android]同步塊代碼中wait和sleep方法持有鎖狀態的區別