Java併發(Runnable+Thread)實現硬盤文件搜索

目錄

零、插播2020CSDN博客之星投票新聞

一、承上啓下

二、Java中的多線程

1、擴展Thread類並重載run()方法

2、實現Runnable接口

三、串行文件搜索

1、創建公共類Result保存搜索結果。

2、查找算法

 四、並行文件搜索(多線程)

1、創建ParallelGroupFileTask類。

2、多線程算法

五、結果

1、串行(單線程)

2、併發(多線程)


零、插播2020CSDN博客之星投票新聞

近日(1月11日-1月24日),2020CSDN博客之星評選正在火熱進行中,作爲碼齡1年的小白有幸入選Top 200,首先很感謝CSDN官方把我選上,本來以爲只是來湊熱鬧,看大佬們PK 。

綜合過去9天大佬們戰況,前10名大佬基本坐得很穩,後期出現黑馬發力,勢不可擋,都在衝刺Top 20,有了微妙的變化,不得不令人佩服點贊!真正的實力可以看出,文章數量不重要,更重要的是質量!一切用數據說話,如圖:

截至 2021-01-20 11:50:02 

看了大佬的驚人數據,與我差距甚大,不禁感慨,接下來看看我自己

首先,很感謝每一位幫忙投票的粉絲和兄弟姐妹們,感謝您的關注和支持,經過大家上一週的共同努力,我已進入2020博客之星投票排行榜Top 100。

投票還有一週時間,進入更激烈更有懸念的階段,希望讀者們下來一週能投出您手中寶貴的票權,讓我更進一步!

 

投票地址:https://bss.csdn.net/m/topic/blog_star2020/detail?username=charzous

或者掃碼投票:

 

重點:每一個投票都會被記錄,投了之後找Charzous幫忙也容易了(瘋狂暗示投票拉票)!

比如,幫忙下載資源,或者博客一鍵三連,再次對每位幫我投票的粉絲表示感謝! 😊新的一年,讓我們一起變得更強!

即日起到24號,每天都可以投票哦,票數越多,貢獻排行榜就越靠前,我就記住你的名字啦!

24號是否能和大佬們在頂峯相見,就靠大家了哈!

 

一、承上啓下

前一篇學習了Java併發程序設計原理之後,爲了對這個部分有了更深層的理解,並運用於實際場景中,所以我找了比較實際的案例進行實踐——文件搜索,簡單來說,這也是電腦文件系統中的一個常見功能,用戶可以通過用戶名搜索文件系統中符合條件的文件。

文件搜索的程序需要用到Java併發API中的Thread類和Runnable接口,其中一些重要的內容先簡單瞭解一下。

二、Java中的多線程

線程類Thread,有兩種方式創建執行線程。

1、擴展Thread類並重載run()方法

Thread類包含了豐富的方法,在實現線程時候必須重載run方法,擴展Thread類和調用start方法創建新的線程。其他常用方法:

  1. getId():獲取Thread對象的標識符,線程整個生命週期中唯一不變的一個正整數。
  2. getName()/setName():String類型,獲取或設置Thread對象名。
  3. getPriority()/setPriority():獲取或設置線程的優先級。值範圍:Thread.MIN_PRIORITY~Thread.MAX_PRIORITY(1~10),創建時默認Thread.NORM_PRIORITY(5)
  4. getState():線程對象的狀態。包括:NEW(新創建)、RUNNABLE(運行中)、BLOCKED(等待鎖定)、WAITING(等待)、TIME_WAITING(有時間限制等待)、THREAD(完成)。線程在一段時間中只能處於一種狀態,而且是在JVM中的狀態,不能映射到操作系統的線程狀態。
  5. interrupt():請求結束執行Thread對象。
  6. interrupted():檢查中斷狀態,清除中斷標誌的值。
  7. isInterrupted():檢查中斷狀態,不清除中斷標誌的值。
  8. sleep():線程執行睡眠時間,單位毫秒。
  9. join():暫停調用線程的執行,直到調用該方法的線程執行結束爲止。
  10. currentThread():靜態方法,返回實際執行當前任務的Thread對象。

2、實現Runnable接口

可以通過線程來執行Runnable對象,更靈活更改併發程序,還可以通過不同線程使用同一個Runnable對象。

相對來說,使用Runnable接口創建線程的方法更加推薦,它只定義了run方法,是每個線程的主方法。當執行start方法啓動新線程時,就會調用run方法。

三、串行文件搜索

這裏分爲兩種版本,串行(單線程)和併發(多線程),後續可以進行比較。

1、創建公共類Result保存搜索結果

/**
 * Result.java
 * @author Charzous
 * @date 2021/1/20 11:00
 *
 */

package SearchFiles;


public class Result {
    boolean found;
    String path;


    public void setFound(boolean found){
        this.found=found;
    }

    public boolean isFound(){
        return this.found;
    }

    public void setPath(String path){
        this.path=path;
    }

    public String getPath(){
        return this.path;
    }
}

2、查找算法

算法思路簡單,通過初始路徑,獲取文件和目錄內容,並與目標文件名進行比較,相同則記錄Result,算法完成;不同則遞歸遍歷文件,直到算法完成。

/**
 * 
 * SerialSearch.java 
 * @author Charzous
 * @date 2021/1/20 11:15
 *
 */

package SearchFiles;

import java.io.File;

public class SerialFileSearch {
    public static void searchFiles(File file,String fileName,Result result){
        File[] contents;
        contents=file.listFiles();

        if ((contents==null)||(contents.length==0))
            return;

        for (File content:contents){
            if (content.isDirectory())
                searchFiles(content,fileName,result);
            else{
                if (content.getName().equals(fileName)){
                    result.setPath(content.getAbsolutePath());
                    result.setFound(true);
                    System.out.println("Serial Search Path: "+result.getPath());
                    return;
                }
            }
            if (result.isFound())
                return;
        }
    }

    public static void main(String[] args) {
        Result result=new Result();
        File file=new File("D:\\");
        long startTime=System.currentTimeMillis();
        String fileName="maskOrder.txt";
        SerialFileSearch.searchFiles(file,fileName,result);

        if (!result.isFound())
            System.out.println("未找到該文件:"+fileName);
        else
            System.out.println("找到該文件:"+fileName+"!");
        System.out.println("查詢時間:"+(System.currentTimeMillis()-startTime)+"ms");
    }
}

 四、並行文件搜索(多線程)

1、創建ParallelGroupFileTask

它實現所有用於查找文件的線程,實現Runnable接口,重載run方法,其中包括了處理目錄的processDirectory方法,處理文件的processFile方法

/**
 * ParallelGroupFileTask.java
 * @author Charzous
 * @date 2021/1/20 11:31
 *
 */
package SearchFiles;


import java.io.File;
import java.util.concurrent.ConcurrentLinkedQueue;

class ParallelGroupFileTask implements Runnable {
    private final String fileName;
    private final ConcurrentLinkedQueue<File> directories;
    private final Result parallelResult;
    private boolean found;

    public ParallelGroupFileTask(String fileName, ConcurrentLinkedQueue<File> directories, Result parallelResult) {
        this.fileName = fileName;
        this.directories = directories;
        this.parallelResult = parallelResult;
    }

    @Override
    public void run() {
        while (directories.size() > 0) {
            File file = directories.poll();
            try {
                processDirectory(file,fileName,parallelResult);//遞歸
                if (found) {
                    System.out.println(Thread.currentThread().getName() + " has found the file");
                    System.out.println("parallel search:Path :" + parallelResult.getPath());
                    return;
                }
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + " hae been interrupted");
            }
        }
    }

    public void processDirectory(File file, String fileName, Result parallelResult) throws InterruptedException {
        File[] contents;
        contents = file.listFiles();

        if ((contents == null) || (contents.length == 0))
            return;

        for (File content : contents) {
            if (content.isDirectory()) {
                processDirectory(content, fileName, parallelResult);
                if (Thread.currentThread().isInterrupted())
                    throw new InterruptedException();

                if (found)
                    return;
            } else {
                processFile(content, fileName, parallelResult);//遞歸
                if (Thread.currentThread().isInterrupted())
                    throw new InterruptedException();
                if (found)
                    return;
            }
        }
    }

    public void processFile(File content, String fileName, Result parallelResult) {
        if (content.getName().equals(fileName)) {
            parallelResult.setPath(content.getAbsolutePath());
            this.found = true;
        }
    }

    public boolean getFound() {
        return found;
    }


}

2、多線程算法

創建ParallelGroupFileSearch類,其中包括了存放基本路徑的線程安全的列表ConcurrentLinkedQueue,然後創建新線程,數量有JVM中可用的線程數量,通過Runtime的availableProcessors方法獲得。

其中,若某個線程找到目標文件,會使用interrupt方法取消其他線程的執行。具體實現代碼如下:

/**
 * ParallelGroupFileSearch.java
 * @author Charzous
 * @date 2021/1/20 11:40
 *
 */
package SearchFiles;

import java.io.File;
import java.util.concurrent.ConcurrentLinkedQueue;

public class ParallelGroupFileSearch {
    public static void searchFiles(File file, String fileName, Result parallelResult) {
        ConcurrentLinkedQueue<File> directories = new ConcurrentLinkedQueue<>();
        File[] contents = file.listFiles();

        for (File content : contents) {
            if (content.isDirectory())
                directories.add(content);
        }
        int numThreads = Runtime.getRuntime().availableProcessors();
        Thread[] threads = new Thread[numThreads];

        ParallelGroupFileTask[] tasks = new ParallelGroupFileTask[numThreads];
        for (int i = 0; i < numThreads; i++) {
            tasks[i] = new ParallelGroupFileTask(fileName, directories, parallelResult);
            threads[i] = new Thread(tasks[i]);
            threads[i].start();
        }

        boolean finish = false;

        int numFinished = 0;
        while (!finish) {
            numFinished = 0;
            for (int i = 0; i < threads.length; i++) {
                if (threads[i].getState() == Thread.State.TERMINATED) {
                    numFinished++;
                    if (tasks[i].getFound())
                        finish = true;
                }
            }
            if (numFinished == threads.length)
                finish = true;
        }
        if (numFinished != threads.length) {
            for (Thread thread : threads)
                thread.interrupt();
        }

    }

    public static void main(String[] args) {
        Result result=new Result();
        File file=new File("D:\\");
        String fileName="maskOrder.txt";
        long startTime=System.currentTimeMillis();

        ParallelGroupFileSearch.searchFiles(file,fileName,result);


        System.out.println("查詢時間:"+(System.currentTimeMillis()-startTime)+"ms");
    }

}

五、結果

1、串行(單線程)

串行版本多次測試結果用時在1900ms左右! 

10次測試數據:

查詢時間:1978ms 2036 1860 1926 1861 2100 1889 2030 1905 1990

2、併發(多線程)

併發版本多線程測試用時在1400ms左右!

10次測試數據:

查詢時間:1441ms 1368 1258 1546 1444 1430 1490 1432 1338 1435

從簡單的測試結果可以看出,併發搜索的算法速度提升明顯。

 

這一篇通過實際的案例進行實踐——文件搜索,簡單來說,這也是電腦文件系統中的一個常見功能,用戶可以通過用戶名搜索文件系統中符合條件的文件。Runnable接口和Thread類的基本使用也有了更深的認識。在文件搜索這個案例中,學習了Java併發原理的實際應用,首先設計一種串行的版本,然後再實現併發的版本,這也是一個改進的過程。

如果覺得不錯歡迎“一鍵三連”哦,點贊收藏關注,有問題直接評論,交流學習!


我的CSDN博客:https://blog.csdn.net/Charzous/article/details/112853937

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