• 通过线程池方式改造Stream.parallel()并行流


    在这里插入图片描述

    大家好,我是哪吒。

    上一篇简单聊一聊公平锁和非公平锁,parallel并行流,提到了一个IntStream.rangeClosed并行流问题,很多小伙伴,对这个比较陌生,想用线程池的方式改造一下。

    一、IntStream.rangeClosed并行流

    @Data
    public class LockTest1 {
        public static void main(String[] args) {
            IntStream.rangeClosed(1, 100000).parallel().forEach(i -> new LockTest1().increase());
            System.out.println(time);
        }
    
        private static int time = 0;
    
        private static Object lock = new Object();
        public void increase() {
            synchronized (lock) {
                time++;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    二、线程池方式改造

    不会那些新特性,还是原始的香啊,写起代码,得心应手。

    1、创建线程池

    这时候,有些小伙伴,又陷入了选择恐惧症。用哪个线程池比较好呢?

    简单回顾一下:

    1. 单线程池newSingleThreadExecutor(),只有一个核心线程的线程池,保证任务按FIFO顺序一个个执行;
    2. 固定线程数线程池newFixedThreadPool(10),固定数量的可复用的线程数,来执行任务。当线程数达到最大核心线程数,则加入队列等待有空闲线程时再执行;
    3. 可缓存线程池newCachedThreadPool(),创建的都是非核心线程,而且最大线程数为Interge的最大值,空闲线程存活时间是1分钟。如果有大量耗时的任务,则不适该创建方式,它只适用于生命周期短的任务;
    4. 固定线程数newScheduledThreadPool(10),支持定时和周期性任务newScheduledThreadPool(10),顾名思义,在固定线程数的前提下,添加了定时任务。

    最常用的还是固定线程数线程池newFixedThreadPool(10)。

    @Data
    public class LockTest2 {
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newFixedThreadPool(200);
            for (int i = 0; i < 100000; i++) {
                Thread0926 thread = new Thread0926();
                executorService.execute(thread);
            }
            System.out.println(time);
        }
    
        private static int time = 0;
        private static Object lock = new Object();
        public void increase() {
            synchronized (lock) {
                time++;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2、线程类

    public class Thread0926 implements Runnable{
        @Override
        public void run() {
            LockTest2 lockTest = new LockTest2();
            lockTest.increase();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、信心满满,走起来

    我草,这不对啊,不应该是100000嘛?又把老子整不会了~

    在这里插入图片描述

    三、再次解决并发时i++原子性问题

    上一篇测试过,使用synchronized代码块是可以解决i++线程安全问题的,这次怎么不好使了?

    上面的代码中,synchronized (lock)锁住了time++,lock是静态变量,所以属于类级别的锁。但是新建的线程是一个新的类,超出了锁的范围,所以失效。

    那么,在当前类中,开启线程,是不是就可以了呢?试一下

    public class LockTest4 {
        private static int time = 0;
        private static Object lock = new Object();
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 200; i++) {
                Thread thread = new Thread(() -> {
                    for (int j = 0; j < 500; j++) {
                        synchronized (lock) {
                            time++;
                        }
                    }
                });
                thread.start();
            }
            Thread.sleep(3000);
            System.out.println(time);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    当然,在synchronized代码块中,使用synchronized (LockTest4.class)也是可以的,效果是一样的。

    在这里插入图片描述

    四、并行流与多线程

    并行流的本质的是并行,多线程的本质是并发。

    并行指当多核CPU中的一个CPU执行一个线程时,其它CPU能够同时执行另一个线程,两个线程之间不会抢占CPU资源,可以同时运行。

    并发指在一段时间内CPU处理多个线程,这些线程会抢占CPU资源,CPU资源根据时间片周期在多个线程之间来回切换,多个线程在一段时间内同时运行,而在同一时刻不是同时运行的。

    1、并行和并发的区别?

    1. 并行指多个线程在一段时间的每个时刻都同时运行,并发指多个线程在一段时间内同时运行(不是同一时刻,一段时间内交叉执行)
    2. 并行的多个线程不会抢占系统资源,并发的多个线程会抢占系统资源;
    3. 并行是多CPU的产物,单核CPU中只有并发,没有并行;

    在这里插入图片描述

    2、并行和并发的使用场景

    (1)IO密集场景

    场景应用程序开发,提供http接口、数据库查询、微服务调用都是IO请求,IO请求时几乎不消耗cpu,这是为了提供cup使用率,建议使用多线程并发,线程数可以远大于cpu核数。

    (2)cup密集场景

    对应大量的加减乘除运算、md5、hash等运算操作,需要持续使用cpu,需要让多核cpu并行运算,适合使用forkjoin并行计算

    技术场景多线程不足,使用多线程技术,也能提高性能,但是线程设置过大会浪费cpu线程切换的时间,如果线程任务分配不均匀,会导致有的cpu忙碌有的cpu空闲。

    上一篇:一个关于 i++ 和 ++i 的面试题打趴了所有人

    下一篇:越细粒度的锁越好吗?产生死锁怎么办?

    🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

  • 相关阅读:
    策略模式和模板模式
    Web前端大作业——基于HTML+CSS+JavaScript仿英雄联盟LOL游戏网站
    4年测试经验,面试却突破不了20K,真是太卷了····
    2020中青杯A题集成电路通道布线数学建模全过程论文及程序
    Spring Security基本框架之过滤器
    ChatGPT基础(三) 让ChatGPT回答质量提高十倍的提示词模版
    java健身房管理系统设计计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    ubuntu安装git
    从外包辞职了,600小时后,我入职了字节跳动
    面渣逆袭:二十二图、八千字、二十问,彻底搞定MyBatis!
  • 原文地址:https://blog.csdn.net/guorui_java/article/details/133421224