<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    John Jiang

    a cup of Java, cheers!
    https://github.com/johnshajiang/blog

       :: 首頁 ::  :: 聯系 :: 聚合  :: 管理 ::
      131 隨筆 :: 1 文章 :: 530 評論 :: 0 Trackbacks
    Java并發基礎實踐--退出任務I
    計劃寫一個"Java并發基礎實踐"系列,算作本人對Java并發學習與實踐的簡單總結。本文是該系列的第一篇,介紹了退出并發任務的最簡單方法。(2013.09.25最后更新)

    在一個并發任務被啟動之后,不要期望它總是會執行完成。由于時間限制,資源限制,用戶操作,甚至是任務中的異常(尤其是運行時異常),...都可能造成任務不能執行完成。如何恰當地退出任務是一個很常見的問題,而且實現方法也不一而足。

    1. 任務
    創建一個并發任務,遞歸地獲取指定目錄下的所有子目錄與文件的絕對路徑,最后再將這些路徑信息保存到一個文件中,如代碼清單1所示:
    清單1
    public class FileScanner implements Runnable {

        
    private File root = null;

        
    private List<String> filePaths = new ArrayList<String>();

        
    public FileScanner1(File root) {
            
    if (root == null || !root.exists() || !root.isDirectory()) {
                
    throw new IllegalArgumentException("root must be legal directory");
            }

            
    this.root = root;
        }

        @Override
        
    public void run() {
            travleFiles(root);
            
    try {
                saveFilePaths();
            } 
    catch (Exception e) {
                e.printStackTrace();
            }
        }

        
    private void travleFiles(File parent) {
            String filePath 
    = parent.getAbsolutePath();
            filePaths.add(filePath);

            
    if (parent.isDirectory()) {
                File[] children 
    = parent.listFiles();
                
    for (File child : children) {
                    travleFiles(child);
                }
            }
        }

        
    private void saveFilePaths() throws IOException {
            FileWriter fos 
    = new FileWriter(new File(root.getAbsoluteFile()
                    
    + File.separator + "filePaths.out"));
            
    for (String filePath : filePaths) {
                fos.write(filePath 
    + "\n");
            }
            fos.close();
        }
    }

    2. 停止線程
    有一個很直接,也很干脆的方式來停止線程,就是調用Thread.stop()方法,如代碼清單2所示:
    清單2
    public static void main(String[] args) throws Exception {
        FileScanner task 
    = new FileScanner(new File("C:"));
        Thread taskThread 
    = new Thread(task);
        taskThread.start();

        TimeUnit.SECONDS.sleep(
    1);
        taskThread.stop();
    }
    但是,地球人都知道Thread.stop()在很久很久之前就不推薦使用了。根據官方文檔的介紹,該方法存在著固有的不安全性。當停止線程時,將會釋放該線程所占有的全部監視鎖,這就會造成受這些鎖保護的對象的不一致性。在執行清單2的應用程序時,它的運行結果是不確定的。它可能會輸出一個文件,其中包含部分的被掃描過的目錄和文件。但它也很有可能什么也不輸出,因為在執行FileWriter.write()的過程中,可能由于線程停止而造成了I/O異常,使得最終無法得到輸出文件。

    3. 可取消的任務
    另外一種十分常見的途徑是,在設計之初,我們就使任務是可被取消的。一般地,就是提供一個取消標志或設定一個取消條件,一旦任務遇到該標志或滿足了取消條件,就會結束任務的執行。如代碼清單3所示:
    清單3
    public class FileScanner implements Runnable {

        
    private File root = null;

        
    private List<String> filePaths = new ArrayList<String>();

        
    private boolean cancel = false;

        
    public FileScanner(File root) {
            
        }

        @Override
        
    public void run() {
            
        }

        
    private void travleFiles(File parent) {
            
    if (cancel) {
                
    return;
            }

            String filePath 
    = parent.getAbsolutePath();
            filePaths.add(filePath);

            
    if (parent.isDirectory()) {
                File[] children 
    = parent.listFiles();
                
    for (File child : children) {
                    travleFiles(child);
                }
            }
        }

        
    private void saveFilePaths() throws IOException {
            
        }

        
    public void cancel() {
            cancel 
    = true;
        }
    }
    新的FileScanner實現提供一個cancel標志,travleFiles()會遍歷新的文件之前檢測該標志,若該標志為true,則會立即返回。代碼清單4是使用新任務的應用程序。
    清單4
    public static void main(String[] args) throws Exception {
        FileScanner task 
    = new FileScanner(new File("C:"));
        Thread taskThread 
    = new Thread(task);
        taskThread.start();

        TimeUnit.SECONDS.sleep(
    3);
        task.cancel();
    }
    但有些時候使用可取消的任務,并不能快速地退出任務。因為任務在檢測取消標志之前,可能正處于等待狀態,甚至可能被阻塞著。對清單2中的FileScanner稍作修改,讓每次訪問新的文件之前先睡眠10秒鐘,如代碼清單5所示:
    清單5
    public class FileScanner implements Runnable {

        

        
    private void travleFiles(File parent) {
            
    try {
                TimeUnit.SECONDS.sleep(
    10);
            } 
    catch (InterruptedException e) {
                e.printStackTrace();
            }

            
    if (cancel) {
                
    return;
            }

            
        }

        
    private void saveFilePaths() throws IOException {
            
        }

        
    public void cancel() {
            cancel 
    = true;
        }
    }
    再執行清單3中的應用程序時,可能發現任務并沒有很快速的退出,而是又等待了大約7秒鐘才退出。如果在檢查cancel標志之前要先獲取某個受鎖保護的資源,那么該任務就會被阻塞,并且無法確定何時能夠退出。對于這種情況,就需要使用中斷了。

    4. 中斷
    中斷是一種協作機制,它并不會真正地停止一個線程,而只是提醒線程需要被中斷,并將線程的中斷狀態設置為true。如果線程正在執行一些可拋出InterruptedException的方法,如Thread.sleep(),Thread.join()和Object.wait(),那么當線程被中斷時,上述方法就會拋出InterruptedException,并且中斷狀態會被重新設置為false。任務程序只要恰當處理該異常,就可以正常地退出任務。對清單5再稍作修改,即,如果任務在睡眠時遇上了InterruptedException,那么就取消任務。如代碼清單6所示:
    清單6
    public class FileScanner implements Runnable {

        

        
    private void travleFiles(File parent) {
            
    try {
                TimeUnit.SECONDS.sleep(
    10);
            } 
    catch (InterruptedException e) {
                cancel();
            }

            
    if (cancel) {
                
    return;
            }

            
        }

        
    }
    同時將清單4中的應用程序,此時將調用Thread.interrupt()方法去中斷線程,如代碼清單7所示:
    清單7
    public static void main(String[] args) throws Exception {
        FileScanner3 task 
    = new FileScanner3(new File("C:"));
        Thread taskThread 
    = new Thread(task);
        taskThread.start();

        TimeUnit.SECONDS.sleep(
    3);
        taskThread.interrupt();
    }
    或者更進一步,僅使用中斷狀態來控制程序的退出,而不再使用可取消的任務(即,刪除cancel標志),將清單6中的FileScanner修改成如下:
    清單8
    public class FileScanner implements Runnable {

        

        
    private void travleFiles(File parent) {
            
    try {
                TimeUnit.SECONDS.sleep(
    10);
            } 
    catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }

            
    if (Thread.currentThread().isInterrupted()) {
                
    return;
            }

            
        }

        
    }
    再次執行清單7的應用程序后,新的FileScanner也能即時的退出了。值得注意的是,因為當sleep()方法拋出InterruptedException時,該線程的中斷狀態將又會被設置為false,所以必須要再次調用interrupt()方法來保存中斷狀態,這樣在后面才可以利用中斷狀態來判定是否需要返回travleFiles()方法。當然,對于此處的例子,在收到InterruptedException時也可以選擇直接返回,如代碼清單9所示:
    清單9
    public class FileScanner implements Runnable {

        

        
    private void travleFiles(File parent) {
            
    try {
                TimeUnit.SECONDS.sleep(
    10);
            } 
    catch (InterruptedException e) {
                
    return;
            }

            
        }

        
    }

    5 小結
    本文介紹了三種簡單的退出并發任務的方法:停止線程;使用可取消任務;使用中斷。毫無疑問,停止線程是不可取的。使用可取消的任務時,要避免任務由于被阻塞而無法及時,甚至永遠無法被取消。一般地,恰當地使用中斷是取消任務的首選方式。
    posted on 2013-09-21 19:11 John Jiang 閱讀(2037) 評論(0)  編輯  收藏 所屬分類: JavaSE 、Java 、Concurrency原創 、Java并發基礎實踐
    主站蜘蛛池模板: 特黄aa级毛片免费视频播放| 午夜国产羞羞视频免费网站| 精品一区二区三区免费观看| 亚洲精品第一国产综合野| 亚洲国产另类久久久精品小说| 四虎影视永久免费观看网址| 一个人看的www在线观看免费 | 成年人免费视频观看| 久久青草免费91观看| 国产人成网在线播放VA免费| 亚洲av无码日韩av无码网站冲| 亚洲 欧洲 自拍 另类 校园| 2022年亚洲午夜一区二区福利 | 男人都懂www深夜免费网站| 大片免费观看92在线视频线视频 | 免费大黄网站在线看| 好大好深好猛好爽视频免费| 日韩亚洲国产高清免费视频| 69视频在线观看高清免费| 免费在线看污视频| 国产亚洲精品免费视频播放| 一级做a爰片久久免费| 特级aaaaaaaaa毛片免费视频| 在线精品自拍亚洲第一区| 亚洲欧洲av综合色无码| 亚洲欧美日韩一区二区三区在线| 亚洲av无码专区在线| 国产成人精品亚洲日本在线| 亚洲av无码电影网| 亚洲熟女综合色一区二区三区 | av无码国产在线看免费网站| 欧洲一级毛片免费| 无码区日韩特区永久免费系列 | 亚洲中文字幕无码一去台湾| 亚洲成人激情小说| 亚洲成aⅴ人片久青草影院按摩| 亚洲成AV人片在WWW| 美女被免费视频网站a| 一级人做人爰a全过程免费视频| www成人免费视频| 18禁在线无遮挡免费观看网站|