Java并發(fā)(Runnable+Thread)實(shí)現(xiàn)硬盤(pán)文件搜索功能
零、插播2020CSDN博客之星投票新聞
近日(1月11日-1月24日),2020CSDN博客之星評(píng)選正在火熱進(jìn)行中,作為碼齡1年的小白有幸入選Top 200,首先很感謝CSDN官方把我選上,本來(lái)以為只是來(lái)湊熱鬧,看大佬們PK 。
綜合過(guò)去9天大佬們戰(zhàn)況,前10名大佬基本坐得很穩(wěn),后期出現(xiàn)黑馬發(fā)力,勢(shì)不可擋,都在沖刺Top 20,有了微妙的變化,不得不令人佩服點(diǎn)贊!真正的實(shí)力可以看出,文章數(shù)量不重要,更重要的是質(zhì)量!一切用數(shù)據(jù)說(shuō)話,如圖:
截至 2021-01-20 11:50:02

看了大佬的驚人數(shù)據(jù),與我差距甚大,不禁感慨,接下來(lái)看看我自己!
首先,很感謝每一位幫忙投票的粉絲和兄弟姐妹們,感謝您的關(guān)注和支持,經(jīng)過(guò)大家上一周的共同努力,我已進(jìn)入2020博客之星投票排行榜Top 100。
投票還有一周時(shí)間,進(jìn)入更激烈更有懸念的階段,希望讀者們下來(lái)一周能投出您手中寶貴的票權(quán),讓我更進(jìn)一步!
投票地址:https://bss.csdn.net/m/topic/blog_star2020/detail?username=charzous
或者掃碼投票:


重點(diǎn):每一個(gè)投票都會(huì)被記錄,投了之后找Charzous幫忙也容易了(瘋狂暗示投票拉票)!
比如,幫忙下載資源,或者博客一鍵三連,再次對(duì)每位幫我投票的粉絲表示感謝! 😊新的一年,讓我們一起變得更強(qiáng)!
即日起到24號(hào),每天都可以投票哦,票數(shù)越多,貢獻(xiàn)排行榜就越靠前,我就記住你的名字啦!
24號(hào)是否能和大佬們?cè)陧敺逑嘁?jiàn),就靠大家了哈!
一、承上啟下
前一篇學(xué)習(xí)了Java并發(fā)程序設(shè)計(jì)原理之后,為了對(duì)這個(gè)部分有了更深層的理解,并運(yùn)用于實(shí)際場(chǎng)景中,所以我找了比較實(shí)際的案例進(jìn)行實(shí)踐——文件搜索,簡(jiǎn)單來(lái)說(shuō),這也是電腦文件系統(tǒng)中的一個(gè)常見(jiàn)功能,用戶可以通過(guò)用戶名搜索文件系統(tǒng)中符合條件的文件。
文件搜索的程序需要用到Java并發(fā)API中的Thread類(lèi)和Runnable接口,其中一些重要的內(nèi)容先簡(jiǎn)單了解一下。
二、Java中的多線程
線程類(lèi)Thread,有兩種方式創(chuàng)建執(zhí)行線程。
1、擴(kuò)展Thread類(lèi)并重載run()方法
Thread類(lèi)包含了豐富的方法,在實(shí)現(xiàn)線程時(shí)候必須重載run方法,擴(kuò)展Thread類(lèi)和調(diào)用start方法創(chuàng)建新的線程。其他常用方法:
getId():獲取Thread對(duì)象的標(biāo)識(shí)符,線程整個(gè)生命周期中唯一不變的一個(gè)正整數(shù)。getName()/setName():String類(lèi)型,獲取或設(shè)置Thread對(duì)象名。
getPriority()/setPriority():獲取或設(shè)置線程的優(yōu)先級(jí)。值范圍:Thread.MIN_PRIORITY~Thread.MAX_PRIORITY(1~10),創(chuàng)建時(shí)默認(rèn)Thread.NORM_PRIORITY(5)。getState():線程對(duì)象的狀態(tài)。包括:NEW(新創(chuàng)建)、RUNNABLE(運(yùn)行中)、BLOCKED(等待鎖定)、WAITING(等待)、TIME_WAITING(有時(shí)間限制等待)、THREAD(完成)。
線程在一段時(shí)間中只能處于一種狀態(tài),而且是在JVM中的狀態(tài),不能映射到操作系統(tǒng)的線程狀態(tài)。interrupt():請(qǐng)求結(jié)束執(zhí)行Thread對(duì)象。
interrupted():檢查中斷狀態(tài),清除中斷標(biāo)志的值。
isInterrupted():檢查中斷狀態(tài),不清除中斷標(biāo)志的值。
sleep():線程執(zhí)行睡眠時(shí)間,單位毫秒。
join():暫停調(diào)用線程的執(zhí)行,直到調(diào)用該方法的線程執(zhí)行結(jié)束為止。
currentThread():靜態(tài)方法,返回實(shí)際執(zhí)行當(dāng)前任務(wù)的Thread對(duì)象。
2、實(shí)現(xiàn)Runnable接口
可以通過(guò)線程來(lái)執(zhí)行Runnable對(duì)象,更靈活更改并發(fā)程序,還可以通過(guò)不同線程使用同一個(gè)Runnable對(duì)象。
相對(duì)來(lái)說(shuō),使用Runnable接口創(chuàng)建線程的方法更加推薦,它只定義了run方法,是每個(gè)線程的主方法。當(dāng)執(zhí)行start方法啟動(dòng)新線程時(shí),就會(huì)調(diào)用run方法。
三、串行文件搜索
這里分為兩種版本,串行(單線程)和并發(fā)(多線程),后續(xù)可以進(jìn)行比較。
1、創(chuàng)建公共類(lèi)Result保存搜索結(jié)果
/**
* 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、查找算法
算法思路簡(jiǎn)單,通過(guò)初始路徑,獲取文件和目錄內(nèi)容,并與目標(biāo)文件名進(jìn)行比較,相同則記錄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("查詢時(shí)間:"+(System.currentTimeMillis()-startTime)+"ms");
}
}
四、并行文件搜索(多線程)
1、創(chuàng)建ParallelGroupFileTask類(lèi)
它實(shí)現(xiàn)所有用于查找文件的線程,實(shí)現(xiàn)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、多線程算法
創(chuàng)建ParallelGroupFileSearch類(lèi),其中包括了存放基本路徑的線程安全的列表ConcurrentLinkedQueue,然后創(chuàng)建新線程,數(shù)量有JVM中可用的線程數(shù)量,通過(guò)Runtime的availableProcessors方法獲得。
其中,若某個(gè)線程找到目標(biāo)文件,會(huì)使用interrupt方法取消其他線程的執(zhí)行。具體實(shí)現(xiàn)代碼如下:
/**
* 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("查詢時(shí)間:"+(System.currentTimeMillis()-startTime)+"ms");
}
}
五、結(jié)果
1、串行(單線程)
串行版本多次測(cè)試結(jié)果用時(shí)在1900ms左右!
10次測(cè)試數(shù)據(jù):
查詢時(shí)間:1978ms 2036 1860 1926 1861 2100 1889 2030 1905 1990


2、并發(fā)(多線程)
并發(fā)版本多線程測(cè)試用時(shí)在1400ms左右!
10次測(cè)試數(shù)據(jù):
查詢時(shí)間:1441ms 1368 1258 1546 1444 1430 1490 1432 1338 1435


從簡(jiǎn)單的測(cè)試結(jié)果可以看出,并發(fā)搜索的算法速度提升明顯。
這一篇通過(guò)實(shí)際的案例進(jìn)行實(shí)踐——文件搜索,簡(jiǎn)單來(lái)說(shuō),這也是電腦文件系統(tǒng)中的一個(gè)常見(jiàn)功能,用戶可以通過(guò)用戶名搜索文件系統(tǒng)中符合條件的文件。Runnable接口和Thread類(lèi)的基本使用也有了更深的認(rèn)識(shí)。在文件搜索這個(gè)案例中,學(xué)習(xí)了Java并發(fā)原理的實(shí)際應(yīng)用,首先設(shè)計(jì)一種串行的版本,然后再實(shí)現(xiàn)并發(fā)的版本,這也是一個(gè)改進(jìn)的過(guò)程。
到此這篇關(guān)于Java并發(fā)(Runnable+Thread)實(shí)現(xiàn)硬盤(pán)文件搜索的文章就介紹到這了,更多相關(guān)Java并發(fā)硬盤(pán)文件搜索內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java 實(shí)現(xiàn)并發(fā)的幾種方式小結(jié)
- Java并發(fā)編程之ConcurrentLinkedQueue源碼詳解
- JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處詳解
- Java并發(fā)編程之CountDownLatch源碼解析
- Java并發(fā)編程之線程之間的共享和協(xié)作
- Java并發(fā)編程之Exchanger方法詳解
- java關(guān)于并發(fā)模型中的兩種鎖知識(shí)點(diǎn)詳解
- 如何使用JCTools實(shí)現(xiàn)Java并發(fā)程序
- Java并發(fā)編程之ReadWriteLock讀寫(xiě)鎖的操作方法
- Java高并發(fā)BlockingQueue重要的實(shí)現(xiàn)類(lèi)詳解
- Java基礎(chǔ)之并發(fā)相關(guān)知識(shí)總結(jié)
相關(guān)文章
mybatis-plus分頁(yè)查詢的實(shí)現(xiàn)實(shí)例
頁(yè)查詢是一項(xiàng)常用的數(shù)據(jù)庫(kù)查詢方法,本文主要介紹了mybatis-plus分頁(yè)查詢的實(shí)現(xiàn)實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
Spring中數(shù)據(jù)訪問(wèn)對(duì)象Data Access Object的介紹
今天小編就為大家分享一篇關(guān)于Spring中數(shù)據(jù)訪問(wèn)對(duì)象Data Access Object的介紹,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01
詳解Java項(xiàng)目中讀取properties文件
本篇文章主要介紹了Java項(xiàng)目中讀取properties文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-12
解決Springboot不能自動(dòng)提交數(shù)據(jù)庫(kù)連接問(wèn)題
在使用SSM框架開(kāi)發(fā)時(shí),若在同一Service內(nèi)部方法間互相調(diào)用,直接使用this關(guān)鍵字會(huì)導(dǎo)致事務(wù)管理失效,從而引發(fā)如數(shù)據(jù)庫(kù)連接不足等問(wèn)題,原因是通過(guò)this調(diào)用不會(huì)經(jīng)過(guò)Spring的代理,因此不會(huì)自動(dòng)進(jìn)行事務(wù)處理2024-09-09
SpringMVC前端和后端數(shù)據(jù)交互總結(jié)
本篇文章主要介紹了SpringMVC前端和后端數(shù)據(jù)交互總結(jié),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
Java生成Echarts表圖的2種實(shí)現(xiàn)方案
這篇文章主要給大家介紹了關(guān)于Java生成Echarts表圖的2種實(shí)現(xiàn)方案,ECharts是一款功能非常強(qiáng)大的JavaScript圖表庫(kù),文中通過(guò)代碼實(shí)例介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09
Java攔截器Interceptor和過(guò)濾器Filte的執(zhí)行順序和區(qū)別
本文主要介紹了Java攔截器Interceptor和過(guò)濾器Filte的執(zhí)行順序和區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
springboot controller 增加指定前綴的兩種實(shí)現(xiàn)方法
這篇文章主要介紹了springboot controller 增加指定前綴的兩種實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02

