標籤:nbsp lap 分享 close 情況 個數 ati 對象 his
工作中遇到的問題,記錄下解決的思路
問題:
對磁碟進行片段化測試(比如說,磁碟空間是16G),從64K開始寫檔案,寫滿後刪除一半,然後寫32K 的資料,寫滿後刪除一半。。。直到4K寫滿刪除一般算是結束
第一階段:
使用單獨的一個線程進行操作,先寫資料,然後刪除資料,用迴圈控制跳出
代碼
public class Task extends Thread{ public void run() { int size = 64; while(size >=4){ write(size); delete(); size /= 2; } } private void write(int size){ //省略具體的寫資料方法,判斷是否寫滿 } private void delete(){ //省略刪除的方法 }}View Code
上述的代碼已經實現了功能,但是如果空間很大,怎麼半?一個線程寫的也太慢了,如何提高效率,寫入的速度?
辦法就是使用多線程,多個線程同時寫,肯定能提升不小的效率
第二階段:
使用多個線程進行操作,代碼和第一階段的代碼沒有變化,共同寫同一個磁碟就行
然後出現了新的問題,多線程操作,到最後肯定會出現剩餘的磁碟空間就夠一個線程使用(假如是線程A ),其他線程已經寫完了該階段的內容(假如是線程B,C,D),開始執行刪除操作了,此時,這4個線程有的在寫入資料,有的在刪除資料,會導致線程A一直讀到有剩餘空間可以寫入(因為其他線程在刪除檔案,騰出新的空間),這樣到最後4K的時候,就出現了A 線程還在寫,其他的線程都已經停止好久了。又浪費了好多時間。
如何解決呢?我想到了使用線程的同步,A線程寫完了就等其他線程,等到所有的線程都寫完了,大家一起開始刪除,等到大家都刪除完成了,再一起開始下一個階段的寫入
那該如何等待呢?
我用了Object類的wait()和notifyAll()方法。
第三階段:
使用同步解決其他線程結束,就剩餘一個線程寫的問題,進一步的提高效率
代碼
import java.util.HashMap;import java.util.Map;/** * 狀態資訊類 */public class Status { private int finishCount = 0; private int threadCount = 0; // 儲存每個線程的狀態 private Map<Integer, Boolean> maps; public Status(int threadCount) { this.maps = new HashMap<Integer, Boolean>(); this.threadCount = threadCount; } //更新當前線程是否在等待狀態,status = true表示已經在等待了 public synchronized void setStatus(int threadIndex, boolean statu) { maps.put(threadIndex, statu); } public boolean getStatus() { boolean result = true; for (Map.Entry<Integer, Boolean> entry : maps.entrySet()) { result = result && entry.getValue(); } return result; } // 更新已經完成的線程個數(全部各個階段執行完調用) public synchronized void updateFinishCount() { this.finishCount += 1; } public int getFinishCount() { return finishCount; }}View Code
/** * 觀察線程 */public class Watcher extends Thread{ private int threadCount = 0; private Status status; public Watcher(Status status,int threadCount ) { this.status = status; this.threadCount = threadCount; } public void run() { while(true){ //檢查是否所有status對象上的線程是否都在等待 if(status.getStatus()){ status.notifyAll(); } //檢查是否所有線程全部執行完成 if(status.getFinishCount() == threadCount){ break; } } }}View Code
/** * 具體執行任務的線程 */public class Task extends Thread{ private Status status; private int index; public Task(Status status,int index) { this.status = status; this.index =index; } public void run() { int size = 64; while(size >=4){ write(size); synchronized (status) { status.setStatus(index, true);//設定為等待狀態 try { status.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } status.setStatus(index, false);//取消等待狀態 delete(); synchronized (status) { status.setStatus(index, true);//設定為等待狀態 try { status.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } status.setStatus(index, false);//取消等待狀態 size /= 2; } } private void write(int size){ //省略具體的寫資料方法,判斷是否寫滿 } private void delete(){ //省略刪除的方法 }}View Code
現在看起來是完美了,但是實際的運行過程中,會發現,真的沒有控制住線程的同步,還是出現了之前的第二階段的問題,有一個線程比其他線程慢,而且出現了一個線程沒有按照依次遞減的順序執行的古怪情況,我想應該是沒有真正的同步造成的。之後又去尋找資料,發現Java提供了一個類,就像是為這種情況量身定做的。它就是 CyclicBarrier ,它自己維護了一個計數器,每當調用一次await()方法,就會阻塞當前的線程,並且計數器減一,計數器的值來源於構造方法,計數器為0的時候,就解除阻塞,更好的是,當計數器為0時,再調用await()方法的時候,會將計數器變成初始值減一,重新開始一個迴圈。
第四階段:
** * 具體執行任務的線程 */public class Task extends Thread { private CyclicBarrier cyclicBarrier; public Task(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } public void run() { int size = 64; while (size >= 4) { write(size); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { //捕獲到該異常的話,表示這個線程不用等待了,需要處理一下,喚醒其他阻塞的線程 e.printStackTrace(); } delete(); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } size /= 2; } } private void write(int size) { // 省略具體的寫資料方法,判斷是否寫滿 } private void delete() { // 省略刪除的方法 }}View Code
測試的代碼
public class Test { private static final int THREAD_COUNT = 4; public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT); for(int i=0;i<THREAD_COUNT;i++){ new Task(barrier).start(); } }}View Code
Java 多線程使用