多線程:生產者消費者模型,心得筆記

題目

簡介,多線程讀取文件,消費者過濾排序
具體說明:
假設本地有一個文件夾,文件夾下面有若干文件,

文件格式如下:
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

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