<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

       :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理 ::
      131 隨筆 :: 1 文章 :: 530 評論 :: 0 Trackbacks
    Java并發(fā)基礎(chǔ)實踐--退出任務(wù)II
    本系列上一篇中所述的退出并發(fā)任務(wù)的方式都是基于JDK 5之前的API,本文將介紹使用由JDK 5引入的并發(fā)工具包中的API來退出任務(wù)。(2013.10.08最后更新)

        在本系列的前一篇中講述了三種退出并發(fā)任務(wù)的方式--停止線程;可取消的任務(wù);中斷,但都是基于JDK 5之前的API。本篇將介紹由JDK 5引入的java.concurrent包中的Future來取消任務(wù)的執(zhí)行。

    1. Future模式
        Future是并發(fā)編程中的一種常見設(shè)計模式,它相當于是Proxy模式與Thread-Per-Message模式的結(jié)合。即,每次都創(chuàng)建一個單獨的線程去執(zhí)行一個耗時的任務(wù),并且創(chuàng)建一個Future對象去持有實際的任務(wù)對象,在將來需要的時候再去獲取實際任務(wù)的執(zhí)行結(jié)果。
    依然先創(chuàng)建一個用于掃描文件的任務(wù)FileScannerTask,如代碼清單1所示,
    清單1
    public class FileScannerTask implements Runnable {

        
    private File root = null;

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

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

            
    this.root = root;
        }

        @Override
        
    public void run() {
            travleFiles(root);
        }

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

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

        
    public List<String> getFilePaths() {
            
    return (List<String>) filePaths.clone();
        }
    }
    此處的文件掃描任務(wù),提供了一個getFilePaths()方法以允許隨時都可以取出當前已掃描過的文件的路徑(相當于一個任務(wù)快照)。然后,創(chuàng)建一個針對該任務(wù)的Future類,如代碼清單2所示,
    清單2
    public class FileScannerFuture {

        
    private FileScannerTask task = null;

        
    public FileScannerFuture(FileScannerTask task) {
            
    new Thread(task).start();
            
    this.task = task;
        }

        
    public List<String> getResult() {
            
    return task.getFilePaths();
        }
    }
    FileScannerFuture持有FileScannerTask的引用,并創(chuàng)建一個獨立的線程來執(zhí)行該任務(wù)。在任務(wù)的執(zhí)行過程中,應(yīng)用程序可以在"未來"的某個時刻去獲取一個任務(wù)的快照,如代碼清單3所示,
    清單3
    public static void main(String[] args) throws Exception {
        FileScannerFuture future 
    = new FileScannerFuture(new FileScannerTask(new File("C:")));

        TimeUnit.SECONDS.sleep(
    1);
        List
    <String> filePaths1 = future.getResult();
        System.out.println(filePaths1.size());

        TimeUnit.SECONDS.sleep(
    1);
        List
    <String> filePaths2 = future.getResult();
        System.out.println(filePaths2.size());
    }

    2. 使用并發(fā)工具包中的Future實現(xiàn)
        前面所展示的Future實現(xiàn)十分的簡陋,沒有實際應(yīng)用的意義。使用FileScannerFuture,應(yīng)用程序在獲取filePaths時,無法得知其獲取的是否為最終結(jié)果,即無法判斷FileScannerTask是否已經(jīng)完成。而且,也不能在必要時停止FileScannerTask的執(zhí)行。毫無疑問,由JDK 5引入的并發(fā)工具包肯定會提供此類實用工具,如FutureTask。為了使用并發(fā)工具包中的Future,需要修改前述的FileScannerTask實現(xiàn),讓其實現(xiàn)Callable接口,如代碼清單4所示,
    清單4
    public class FileScannerTask implements Callable<List<String>> {

        
    private File root = null;

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

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

            
    this.root = root;
        }

        @Override
        
    public List<String> call() {
            travleFiles(root);
            
    return filePaths;
        }

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

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

        
    public List<String> getFilePaths() {
            
    return (List<String>) filePaths.clone();
        }
    }
    應(yīng)用程序也要相應(yīng)的修改成如代碼清單5所示,使用ExecutorService來提交任務(wù),并創(chuàng)建一個Future/FutureTask實例。
    清單5
    public static void main(String[] args) {
        ExecutorService executorService 
    = Executors.newCachedThreadPool();
        Future
    <List<String>> future = executorService.submit(new FileScannerTask(new File("C:")));

        
    try {
            List
    <String> filePaths = future.get();
            System.out.println(filePaths.size());
        } 
    catch (InterruptedException e) {
            e.printStackTrace();
        } 
    catch (ExecutionException e) {
            e.printStackTrace();
        }

        executorService.shutdown();
    }
    此處就是調(diào)用Future.get()方法來獲取任務(wù)的執(zhí)行結(jié)果,如果任務(wù)沒有執(zhí)行完畢,那么該方法將會被阻塞。該Future實現(xiàn)的好處就是,正常情況下,只有在任務(wù)執(zhí)行完畢之后才能獲取其結(jié)果,以保證該結(jié)果是最終執(zhí)行結(jié)果。

    3. 使用Future取消任務(wù)
        Future除了定義有可獲取執(zhí)行結(jié)果的get方法(get()以及get(long timeout, TimeUnit unit)),還定義了三個方法:cancel(),isCancelled()以及isDone(),用于取消任務(wù),以及判定任務(wù)是否已被取消、已執(zhí)行完畢。如代碼清單6所示,
    清單6
    public interface Future<V> {

        
    boolean cancel(boolean mayInterruptIfRunning);
        
    boolean isCancelled();
        
    boolean isDone();
        
    }
    其中,cancel()方法中的boolean參數(shù)若為true,表示在取消該任務(wù)時,若執(zhí)行該任務(wù)的線程仍在運行中,則對其進行中斷。如代碼清單7所示,若任務(wù)執(zhí)行超時了,那么就取消它。
    清單7
    public static void main(String[] args) {
        ExecutorService executorService 
    = Executors.newCachedThreadPool();
        Future
    <List<String>> future = executorService.submit(new FileScannerTask(new File("C:")));

        
    try {
            List
    <String> filePaths = future.get(1, TimeUnit.SECONDS);
            System.out.println(filePaths.size());
        } 
    catch (InterruptedException e) {
            e.printStackTrace();
        } 
    catch (ExecutionException e) {
            e.printStackTrace();
        } 
    catch (TimeoutException e) {
            e.printStackTrace();
        } 
    finally {
            future.cancel(
    true);
        }

        executorService.shutdown();
    }
    在實際應(yīng)用中,取消任務(wù)的原由肯定不僅僅只是超時這么簡單,還可能是由于接受到了用戶的指令。此時,則可能會從另一個獨立線程去取消該任務(wù)。除了取消任務(wù)之外,有時還需要取出任務(wù)中已經(jīng)生成的部分結(jié)果。但為了能夠響應(yīng)任務(wù)的退出,首先需要修改FileScannerTask,使得當任務(wù)被取消(中斷)時,任務(wù)能夠真正的快速停止并返回,如代碼清單8所示,
    清單8
    public class FileScannerTask implements Callable<List<String>> {

        

        
    private void travleFiles(File parent) {
            
    if (Thread.currentThread().isInterrupted()) {
                
    return;
            }

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

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

        
    }
    相應(yīng)地修改應(yīng)用程序的代碼,如代碼清單9所示,
    清單9
    public static void main(String[] args) {
        ExecutorService executorService 
    = Executors.newCachedThreadPool();
        FileScannerTask task 
    = new FileScannerTask(new File("C:"));
        
    final Future<List<String>> future = executorService.submit(task);
        
        
    new Thread(new Runnable() {
            
            @Override
            
    public void run() {
                
    try {
                    TimeUnit.SECONDS.sleep(
    1);
                } 
    catch (InterruptedException e) {
                    e.printStackTrace();
                }
                future.cancel(
    true);
            }
        }).start();
        
        
    try {
            List
    <String> filePaths = future.get();
            System.out.println(filePaths.size());
        } 
    catch (InterruptedException e) {
            e.printStackTrace();
        } 
    catch (ExecutionException e) {
            e.printStackTrace();
        } 
    catch (CancellationException e) {
            List
    <String> filePaths = task.getFilePaths();
            System.out.println(
    "Partly result: " + filePaths.size());
        }
        
        executorService.shutdown();
    }
    由上可知,此處使用Future.cancel(true)的本質(zhì)依然是利用了線程的中斷機制。

    4. 小結(jié)
        使用Future可以在任務(wù)啟動之后的特定時機再去獲取任務(wù)的執(zhí)行結(jié)果。由JDK 5引入的并發(fā)工具包中提供的Future實現(xiàn)不僅可以獲取任務(wù)的執(zhí)行結(jié)果,還可以用于取消任務(wù)的執(zhí)行。
    posted on 2013-10-07 16:55 John Jiang 閱讀(3313) 評論(3)  編輯  收藏 所屬分類: Java 、Concurrency原創(chuàng) 、Java并發(fā)基礎(chǔ)實踐

    評論

    # re: Java并發(fā)基礎(chǔ)實踐--退出任務(wù)II(原) 2013-10-08 11:04 xdemo.cn
    哪抄的?繼續(xù)繼續(xù)!  回復  更多評論
      

    # re: Java并發(fā)基礎(chǔ)實踐--退出任務(wù)II(原) 2013-10-08 16:22 Sha Jiang
    @xdemo.cn
    每個字,每行代碼都是我自己親自寫的:-(  回復  更多評論
      

    # re: Java并發(fā)基礎(chǔ)實踐--退出任務(wù)II(原)[未登錄] 2013-10-16 14:38 程序員
    @Sha Jiang
    厲害,期待寫更多的好文  回復  更多評論
      

    主站蜘蛛池模板: 免费看国产一级特黄aa大片| 91免费国产在线观看| 久久久久久亚洲精品不卡| 一级特级女人18毛片免费视频| 亚洲精品字幕在线观看| 91九色老熟女免费资源站| 亚洲日韩一区二区一无码| 亚洲色欲久久久久综合网| 久久国产乱子免费精品| 亚洲午夜无码毛片av久久京东热| 亚洲精品无码av天堂| 久久综合给合久久国产免费| 亚洲熟妇无码AV| 亚洲国产午夜精品理论片 | 亚洲香蕉网久久综合影视| 国产精品视频免费观看| 久久免费线看线看| 亚洲精品GV天堂无码男同| 久久久久久久尹人综合网亚洲| 野花高清在线电影观看免费视频| 国产国产人免费人成成免视频 | 免费观看黄色的网站| 久久精品毛片免费观看| 狠狠综合亚洲综合亚洲色| 亚洲伦理一区二区| 国产又大又粗又硬又长免费| 一本无码人妻在中文字幕免费| 五月婷婷综合免费| 四虎国产精品免费永久在线| 亚洲日产乱码一二三区别| 亚洲av乱码一区二区三区香蕉 | 免费激情网站国产高清第一页| 亚洲欧洲日本精品| 在线亚洲午夜理论AV大片| 成人毛片免费播放| 99视频免费播放| 1000部啪啪毛片免费看| 中国国语毛片免费观看视频| 亚洲GV天堂无码男同在线观看| 亚洲heyzo专区无码综合| 无套内射无矿码免费看黄|