題目
簡介,多線程讀取文件,消費者過濾排序
具體說明:
假設本地有一個文件夾,文件夾下面有若干文件,
文件格式如下:
2000102,100,98.3
2000103,101,73.3
2000104,102,98.3
2000105,100,101.3
2000106,101,45.3
文件格式說明:
每行由三列構成,
第一列:id,
第二列:groupId,
第三列:指標quota
功能要求:
1.把所有文件裏面的內容按照分組進行排序,
2.所有文件按照分組排序之後,輸出每個分組下面的最小指標值。
非功能要求:
1.文件讀取要有線程池來執行,線程池的大小固定爲10,文件內容需要存儲到指定的內容數據結構當中
2.查找要求有獨立線程來執行,直接消費讀取線程池產生的內存數據結構。
3.文件讀取和排序要求併發作業,文件讀取只要產生了數據,就可以把數據交給排序線程進行消費,計算最小值。
代碼要求
1.重上面的要求語意裏面抽象出合適的設計模式。
2.需要考慮多線程的併發控制,同步機制。
3.代碼實現只能用JDK1.6或者1.8自帶的工具類
————————————————
版權聲明:本文爲CSDN博主「ZK_小姜」的原創文章,遵循 CC 4.0 BY-SA
版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/u014039577/article/details/82387302
思路
1.首先類分爲生產者/消費者
2.生產者把任務拆分爲多線程執行,具體執行線程爲Worker。使用CountDownLatch的await方法,等待所有線程結束,後退出。
3.消費者使用阻塞隊列,收到退出命令後,等待消費完畢,然後中斷阻塞隊列的take阻塞。
4.關於Demo,使用join等待生產者的所有任務完成後,控制消費者退出。
注意:此處join必須結合CountDownLatch。否則生產者分配任務,開啓所有子線程後,會直接退出。子任務繼續執行。
5.關於interrupt,中斷拋出異常後,中斷狀態會重置爲“未中斷”,
所以while (!isInterrupted())不一定能退出循環,需要在catch處重新interrupt
6.關於TreeMap,put操作時增加了類鎖,保證安全性問題。適用於多消費者.
已知hashMap在多線程中會導致死循環,佔用超高cpu
7.關於消費者退出方法exit(),此處必須使用this.interrupt();
區別於 Thread.currentThread().interrupt(); 由於exit()在main方法中被調用,
中斷的是主線程
存儲數據的類
package threads.productConsumer;
import com.sun.deploy.util.BlackList;
import java.util.TreeMap;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @description:
* @author: HYW
* @create: 2020-01-21 11:30
*/
public class DataWare {
static LinkedBlockingQueue<DataItem> sourceQueue=new LinkedBlockingQueue<DataItem>();
static TreeMap<String,Float> sortMap=new TreeMap<String,Float>();
public static LinkedBlockingQueue<DataItem> getSourceQueue() {
return sourceQueue;
}
public static void setSourceQueue(LinkedBlockingQueue<DataItem> sourceQueue) {
DataWare.sourceQueue = sourceQueue;
}
public static TreeMap<String, Float> getSortMap() {
return sortMap;
}
public static void setSortMap(TreeMap<String, Float> sortMap) {
DataWare.sortMap = sortMap;
}
}
java Bean
package threads.productConsumer;
/**
* @description:
* @author: HYW
* @create: 2020-01-21 11:28
*/
public class DataItem {
/**
* id
*/
private String id;
/**
* 分組
*/
private String groupId;
/**
* 指標
*/
private Float quota;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public Float getQuota() {
return quota;
}
public void setQuota(Float quota) {
this.quota = quota;
}
@Override
public String toString() {
return "DataItem{" +
"id='" + id + '\'' +
", groupId='" + groupId + '\'' +
", quota=" + quota +
'}';
}
}
工作線程管理者
package threads.productConsumer;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @description:
* @author: HYW
* @create: 2020-01-21 11:29
*/
public class Producter extends Thread{
String path;
int threadsNum;
ExecutorService threadPool;
Producter(String path,int threadsNum){
this.path=path;
this.threadsNum=threadsNum;
threadPool= Executors.newFixedThreadPool(threadsNum,
new DefaultThreadFactory("product"));;
}
@Override
public void run() {
//獲取文件目錄
File[] fileList=getCataLog();
if(fileList==null ||fileList.length==0){
System.out.println("無文件異常");
return;
}
CountDownLatch countDownLatch=new CountDownLatch(fileList.length);
//遍歷文件
for(File file:fileList){
FileWorker fileWorker=new FileWorker();
fileWorker.setFileName(file.getAbsolutePath());
fileWorker.setCountDownLatch(countDownLatch);
threadPool.execute(fileWorker);
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool.shutdown();
}
private File[] getCataLog() {
File file=new File(path);
return file.listFiles();
}
public static void main(String[] args) {
Producter producter=new Producter("E:\\log",10);
producter.start();
}
}
工作線程
package threads.productConsumer;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @description:
* @author: HYW
* @create: 2020-01-21 14:07
*/
public class FileWorker implements Runnable{
private LinkedBlockingQueue<DataItem> sourceQueue=DataWare.getSourceQueue();
private String fileName;
private CountDownLatch countDownLatch;
public void setFileName(String fileName) {
this.fileName = fileName;
}
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<DataItem> list=getData();
list.forEach(dataItem -> sourceQueue.offer(dataItem));
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName()+"執行完畢");
}
@Test
public List<DataItem> getData() {
List<DataItem> reslist=new ArrayList<>();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(fileName));
String line=null;
while((line=br.readLine())!=null) {
try {
String[] arrs=line.split(",",-1);
DataItem dataItem=new DataItem();
dataItem.setId(arrs[0]);
dataItem.setGroupId(arrs[1]);
dataItem.setQuota(Float.parseFloat(arrs[2]));
reslist.add(dataItem);
}catch (Exception e){
System.out.println("數據解析異常");
e.printStackTrace();
}
}
} catch (IOException e){
e.printStackTrace();
} finally {
try {
if(br!=null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
reslist.forEach(x-> System.out.println(x.toString()));
return reslist;
}
}
消費者
package threads.productConsumer;
import java.util.TreeMap;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @description:
* @author: HYW
* @create: 2020-01-21 11:29
*/
public class Consummer extends Thread{
LinkedBlockingQueue<DataItem> sourceQueue=DataWare.getSourceQueue();
static TreeMap<String,Float> sortMap=DataWare.getSortMap();
@Override
public void run() {
super.run();
while (!isInterrupted()){
try {
DataItem dataItem=sourceQueue.take();
//map爲空
if(sortMap.get(dataItem.getGroupId())==null){
put(dataItem.getGroupId(),dataItem.getQuota());
}else {
float tempMin=sortMap.get(dataItem.getGroupId());
if(tempMin >dataItem.getQuota()){
put(dataItem.getGroupId(),dataItem.getQuota());
}
}
} catch (InterruptedException e) {
System.out.println("中斷狀態"+Thread.currentThread().isInterrupted());
//把中斷狀態 由true 改爲false
System.out.println("中斷狀態"+Thread.interrupted());
//外出循環終止
Thread.currentThread().interrupt();
}
}
}
static synchronized void put(String groupId,float quota){
sortMap.put(groupId,quota);
}
void exit(Thread consumer){
//等待消費結束
while (DataWare.getSourceQueue().size()>0){
System.out.println("剩餘任務數量"+DataWare.getSourceQueue().size());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.interrupt();
}
}
Demo
package threads.productConsumer;
/**
* @description:
* @author: HYW
* @create: 2020-01-21 14:49
*/
public class Demo {
public static void main(String[] args) {
Producter producter=new Producter("E:\\log",10);
producter.start();
//開始消費
System.out.println("開始消費");
Consummer consummer=new Consummer();
consummer.start();
try {
producter.join();
//中斷,消費結束
consummer.exit(consummer);
System.out.println("生產完成;消費結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印結果
DataWare.getSortMap().forEach((key,val)-> System.out.println(key+":"+val));
}
}
輸出結果
開始消費
DataItem{id=‘2000102’, groupId=‘100’, quota=91.32}
DataItem{id=‘2000101’, groupId=‘100’, quota=66.32}
DataItem{id=‘2000103’, groupId=‘100’, quota=101.32}
DataItem{id=‘2000105’, groupId=‘100’, quota=98.32}
DataItem{id=‘2000106’, groupId=‘100’, quota=88.32}
DataItem{id=‘3000102’, groupId=‘101’, quota=98.32}
DataItem{id=‘3000102’, groupId=‘101’, quota=98.32}
DataItem{id=‘3000101’, groupId=‘101’, quota=93.32}
DataItem{id=‘3000101’, groupId=‘101’, quota=93.32}
DataItem{id=‘3000103’, groupId=‘101’, quota=98.32}
DataItem{id=‘3000105’, groupId=‘101’, quota=98.32}
DataItem{id=‘3000103’, groupId=‘101’, quota=98.32}
DataItem{id=‘3000106’, groupId=‘101’, quota=98.32}
DataItem{id=‘3000105’, groupId=‘101’, quota=98.32}
DataItem{id=‘3000106’, groupId=‘101’, quota=98.32}
product-1-2執行完畢
消費成功DataItem{id=‘2000102’, groupId=‘100’, quota=91.32}
product-1-3執行完畢
product-1-1執行完畢
生產已經結束,消費者完成消費後退出
剩餘任務數量14
消費成功DataItem{id=‘3000102’, groupId=‘101’, quota=98.32}
消費成功DataItem{id=‘3000101’, groupId=‘101’, quota=93.32}
消費成功DataItem{id=‘3000103’, groupId=‘101’, quota=98.32}
消費成功DataItem{id=‘3000105’, groupId=‘101’, quota=98.32}
剩餘任務數量10
消費成功DataItem{id=‘3000106’, groupId=‘101’, quota=98.32}
消費成功DataItem{id=‘3000102’, groupId=‘101’, quota=98.32}
消費成功DataItem{id=‘3000101’, groupId=‘101’, quota=93.32}
消費成功DataItem{id=‘3000103’, groupId=‘101’, quota=98.32}
消費成功DataItem{id=‘3000105’, groupId=‘101’, quota=98.32}
剩餘任務數量5
消費成功DataItem{id=‘3000106’, groupId=‘101’, quota=98.32}
消費成功DataItem{id=‘2000101’, groupId=‘100’, quota=66.32}
消費成功DataItem{id=‘2000103’, groupId=‘100’, quota=101.32}
消費成功DataItem{id=‘2000105’, groupId=‘100’, quota=98.32}
消費成功DataItem{id=‘2000106’, groupId=‘100’, quota=88.32}
生產完成;消費結束
100:66.32
101:93.32