并发实践:CountDownLatch + ThreadPoolExecutor

最近在看杨中科老师的课程,

https://www.bilibili.com/video/BV1KB4y1c7V2?p=43&vd_source=2f6c1b009448fb3a45b18078a369d8a0

其中有一个场景,读取一个PDF文件中的所有图片,并将图片输出到一个目录中,使用杨老师封装的包,可以非常容易的实现该需求(基础)。(公司业务中也有类似场景)但是当PDF文件较大,使用单线程处理文件耗时较长,因此考虑多线程实现一个较短耗时的读取图片操作。
线程的创建和销毁消耗资源严重,建议使用线程池进行管理,且某里公司规范要求i需要使用ThreadPoolExecutor来实现线程池。
每个线程负责处理一定页数的PDF文件,并将其中的图片输出到指定目录,当所有线程处理完毕,关闭IO流,符合了CountDownLatch的应用范围。
贴个代码,即将更新代码说明,并修复页码下标越界问题,以下代码性能,由full gc 风险。

public class PDFImages {
    static   ThreadPoolExecutor executor;

    public static void main(String[] args) {
        //ThreadPoolExecutor executor = null;
        executor =  new ThreadPoolExecutor(5,40,100, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy() );

        PDDocument pdf = PDFHelpers.openFile("C:\\Users\\HP\\Desktop\\简历\\深入浅出Spring Boot 2.x.pdf");
        int pageNum = pdf.getNumberOfPages();

        int lacthers = pageNum / 10 + 1 ;
        CountDownLatch latch = new CountDownLatch(lacthers);
        for (int i = 1; i <= lacthers; i++) {
            executor.execute(new ReadPDFThread(i,pdf,latch,pageNum));
        }
        try {
            latch.await();
            pdf.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static class ReadPDFThread implements Runnable{
        int page;
        PDDocument pdf;
        CountDownLatch latch;
        int endPageNum;
        public ReadPDFThread(int page,PDDocument pdf,CountDownLatch latch,int endPageNum){
            this.page = page;
            this.pdf = pdf;
            this.latch = latch;
            this.endPageNum = endPageNum;
        }

        @Override
        public void run() {
            System.out.println("当前线程"+this.page+"执行任务");
            //多线程读取页码范围内的文件,并将其中的图片保存到目标文件夹中
            long startPage = page * 10 - 10 ;
            long endPage = page * 10 ;
            if(startPage + 10 >= endPageNum){
                endPage = endPageNum;
            }
            for(long i=startPage;i<endPage;i++){
                PDPage page = pdf.getPage((int)i);
                List<byte[]> bytes = PDFHelpers.parseImages(page, ".png");
                for(byte[] abyte : bytes){
                    IOHelpers.writeAllBytes("D:/temp/pdfImages/"+i+"ye"+System.currentTimeMillis()+".png",abyte);
                }
            }
            latch.countDown();
        }
    }
}

依赖的包

    <dependency>
            <groupId>com.yzk18</groupId>
            <artifactId>yzk18-docs</artifactId>
            <version>1.3</version>
        </dependency>

任务在线程池中执行的过程
1.工作线程数<核心线程数,向线程池中添加线程,当前任务会被新线程执行。
2.工作线程数>=核心线程数,检查等待队列是否可新增排队任务,任务会加入等待队列。
3.检查到等待队列已满,就会执行拒绝策略,该任务踢出队列或是报错等。

线程池1.png

回收


线程池2.png

常用等待队列

1.有界任务队列,数组实现,需要指定大小,影响效率得关键因素

new ArrayBlockingQueue(size);

2.无界任务队列,链表实现

new LinkedBlockingQueue();

3.优先级队列,加入得任务指定优先级,java中有10级,但是不同操作系统优先级定义不同,当调度优先级抢占式算法,多个代码级别优先级映射到同一个优先级中,例如linux中得7级映射,不推荐使用。
4.同步队列,有任务直接添加新线程执行,直到工作线程数达到最大线程数,无法分配新线程则执行拒绝策略,不常用。

拒绝策略

1.拒绝执行抛异常

new ThreadPoolExecutor.AbortPolicy()

2.拒绝执行不抛异常

 new ThreadPoolExecutor.DiscardPolicy()

3.挤队头任务,不常用

  new ThreadPoolExecutor.DiscardOldestPolicy()

4.谁提交谁执行
保证了任务不会丢失,存在效率降低得问题,
当设置这个策略后,任务队列已满,新任务会被线程调用方会直接执行,出现任务插队情况。

new ThreadPoolExecutor.CallerRunsPolicy()

拒绝策略需要按项目需求选择,例如选择挤掉队头和抛任务策略,任务被挤掉之后可以搭配MQ进行任务补偿,保证结果的最终一致性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容