關於文件讀寫,線程超時終止問題

在對網絡文件下載進行讀寫操作時,有時候會因爲網絡問題導致 “Connection reset” 異常以及有時會在一半的時候卡住,針對這個兩種情況需要重新去下載,第一種異常比較好判斷,可以直接拿錯誤信息即可判斷,第二種情況想到的辦法就是利用線程去解決,給下載單獨開啓一個子線程,並且給該子線程設定一個超時時間,當超過改時間時,則取消下載,中斷該子線程!

在這裏通過實現Callable接口類來設置超時任務:

package com.java.mytest;

import com.csvreader.CsvReader;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import java.io.*;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @Author win7
 * @Date 12/1/16 2:45 PM
 */
public class TestDownloadFile {
    private static String tempUrl = "http://xxxx/test.txt";
    private static String filePath;

    public static void main(String[] args) {
        String suffixName = tempUrl.substring(tempUrl.lastIndexOf("/") + 1, tempUrl.length());
        int timeout = 20; //秒.
        int num = 1;
        Boolean flag = null;
        while (true) {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Boolean result = false;
            DownloadFile downloadFile = new DownloadFile(suffixName, num);
            Future<Boolean> future = executor.submit(downloadFile);// 將任務提交到線程池中
            try {
                future.get(timeout, TimeUnit.SECONDS);// 設定在20秒的時間內完成
            } catch (InterruptedException e) {
                filePath = null;
                System.out.println("線程中斷出錯--------"+num);
            } catch (ExecutionException e) {
                filePath = null;
                System.out.println("線程服務出錯--------"+num);
            } catch (TimeoutException e) {// 超時異常
                filePath = "Connection reset";
                System.out.println("線程服務超時--------"+num);
            } finally {
				//關閉此下載任務
                downloadFile.setIsStop(true);
                flag = future.cancel(true);// 中斷執行此任務的線程
                System.out.println("線程服務關閉--------"+num);
                System.out.println("result is " + result);
				System.out.println("刪除結果:"  + flag);
                executor.shutdownNow();//關閉ExecutorService,阻止等待任務啓動並試圖停止當前正在執行的任務
				//超過3次終止整個任務
                if (num >= 3){
                    break;
                }
            }
            if (StringUtils.isBlank(filePath)){
                System.out.println("download file is error!");
                break;
            }else {
                if ("Connection reset".equalsIgnoreCase(filePath)){
                    System.out.println("============Connection reset continue==========="+num);
                    num++;
                    continue;
                }else {
                    File file = new File(filePath);
                    if (file.exists()){
                        System.out.println("============SUCCESS==========="+num);
                        break;
                    }else {
                        System.out.println("============"+filePath+" continue==========="+num);
                        continue;
                    }
                }
            }
        }
    }


    static class DownloadFile implements Callable<Boolean> {
        private  String fileName;
        private int num;
        //是否關閉此下載任務
        private boolean isStop = false;

        public DownloadFile(String suffixName, int num) {
            this.fileName = suffixName;
            this.num = num;
        }

        /**
         * 將文件下載到本地
         */
        @Override
        public Boolean call(){
            filePath = "F:/" + fileName;
            URL url = null;
            URLConnection conn = null;
            try {
                url = new URL(tempUrl);
                conn = url.openConnection();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (null == url || null == conn){
                filePath = null;
                return false;
            }
            try (
                    //try語句結束後自動關閉資源
                    InputStream inputStream = conn.getInputStream();
                    FileOutputStream outputStream = new FileOutputStream(filePath)
            ) {
                byte[] buffer = new byte[1204];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    if (isStop){
                        break;
                    }
                    outputStream.write(buffer, 0, length);
                }
            }catch (Exception e){
                filePath = e.getMessage();
                e.printStackTrace();
            }finally {
                File file = new File(filePath);
                if (file.exists()){
                    return true;
                }
                return false;
            }
        }

        public void setIsStop(boolean isStop) {
            this.isStop = isStop;
        }
    }
}
記錄下上面代碼遇到的坑:

如果任務超時了,從而執行了future.cancel(true)以及executor.shutdownNow()此時這個線程就應該終結,但是實際上由於用到了IO讀寫操作,並且是阻塞式的,所以這個線程並沒有終止而是繼續的存在的,要知道jvm只有在所有(非守護)線程推出後纔會退出,所以此時的jvm並沒有因爲方法執行完畢而退出。

解決辦法:

1、是在讀寫的時候加上一個是否停止的標識

2、將阻塞式的IO讀寫操作換成不阻塞的NIO讀寫操作(此方法沒去測試,待下次有時間在測)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章