多线程4:线程池、并发、并行、综合案例-抢红包游戏

news2024/11/21 20:22:25

欢迎来到“雪碧聊技术”CSDN博客!

在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目心得。

让我们一同在Java的广阔天地中遨游,携手提升技术能力,共创美好未来!感谢您的关注与支持,期待在“雪碧聊技术”与您共同成长!

目录

一、认识线程池

1、什么是线程池?

2、不复用线程的问题

3、线程池的工作原理

①工作线程

②任务队列

二、创建线程池

1、如何创建线程池?

方式一:JDK5.0开始,提供了代表线程池的接口:ExecutorService。使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。

        举例:

方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。

2、线程池的注意事项

①什么时候开始创建临时线程?

②什么时候会拒绝新任务?

3、任务拒绝策略

三、处理Runnable任务

1、ExecutorService的常用方法

2、总结

四、处理Callable任务

1、ExecutorService的常用方法

2、总结

五、通过Executors工具类,创建线程池

1、该工具类中有哪些静态方法?

2、Executors工具类使用时可能存在的风险

3、总结

六、并发、并行

1、什么是进程?

举例

2、什么是线程?

3、什么是并发?

举例

4、什么是并行?

举例

 5、总结

举例

六、综合案例-抢红包游戏

1、介绍

2、编码


一、认识线程池

1、什么是线程池?

        线程池就是一个可以复用线程的技术。

2、不复用线程的问题

        用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理,创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。

        就好比:每次吃完饭都扔一个碗,长期以来肯定吃不消这么大的消耗。

3、线程池的工作原理

①工作线程

        工作线程:就是线程池里面的线程,来处理任务队列里面的任务,有点类似于饭店的服务员。

②任务队列

        任务队列:里面存放的都是实现了Runnable/Callable接口的任务类,等待工作线程的完成。

二、创建线程池

1、如何创建线程池?

方式一:JDK5.0开始,提供了代表线程池的接口:ExecutorService。使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。

        该实现类 ThreadPoolExecutor的构造器一共有七个参数,如下:

        举例:
public class Test9 {
    public static void main(String[] args) {
        //目标:创建线程池对象来使用
        //1、使用线程池(ExecutorService接口)的实现类ThreadPoolExecutor声明七个参数来创建线程池对象
        ExecutorService pool = new ThreadPoolExecutor(3,5,10, 
                TimeUnit.SECONDS,new ArrayBlockingQueue<>(3), 
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    }
}

方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。

下面五会讲。

2、线程池的注意事项

①什么时候开始创建临时线程?

        新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。

②什么时候会拒绝新任务?

        核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。

3、任务拒绝策略

三、处理Runnable任务

1、ExecutorService的常用方法

        举例:

public class Test9 {
    public static void main(String[] args) {
        //目标:创建线程池对象来使用
        //1、使用线程池(ExecutorService接口)的实现类ThreadPoolExecutor声明七个参数来创建线程池对象
        ExecutorService pool = new ThreadPoolExecutor(3,5,10,
                TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        //2、使用线程池处理任务!看会不会复用线程?
        Runnable target = new My_Runnable();//任务对象
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);

        //3、关闭线程池:一般不关闭线程池
        //pool.shutdown();//等所有任务执行完毕后,再关闭线程池!
        //pool.shutdownNow();//立即关闭,不管任务是否执行完毕!
    }
}

//线程任务类(等待被线程对象执行的任务)
class My_Runnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "执行了~~");
    }
}

        执行结果:

pool-1-thread-2执行了~~
pool-1-thread-3执行了~~
pool-1-thread-2执行了~~
pool-1-thread-3执行了~~
pool-1-thread-2执行了~~
pool-1-thread-1执行了~~

        可见此时6个任务,但是线程池就3个核心线程,因此会产生复用。于是就看到上面确实有很多线程执行了好几个任务,因此线程确实是被复用了。

2、总结

四、处理Callable任务

1、ExecutorService的常用方法

        举例:

        

public class Test10 {
    public static void main(String[] args) {
        //目标:创建线程池对象来使用
        //1、使用线程池(ExecutorService接口)的实现类ThreadPoolExecutor声明七个参数来创建线程池对象
        ExecutorService pool = new ThreadPoolExecutor(3,5,10,
                TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        //2、使用线程池处理任务!看会不会复用线程?
        Future<String> f1 = pool.submit(new My_Callable(100));
        Future<String> f2 = pool.submit(new My_Callable(200));
        Future<String> f3 = pool.submit(new My_Callable(300));
        Future<String> f4 = pool.submit(new My_Callable(400));
        
        //输出线程执行完,返回的结果
        try {
            System.out.println(f1.get());
            System.out.println(f2.get());
            System.out.println(f3.get());
            System.out.println(f4.get());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        //3、关闭线程池:一般不关闭线程池
        //pool.shutdown();//等所有任务执行完毕后,再关闭线程池!
        //pool.shutdownNow();//立即关闭,不管任务是否执行完毕!
    }
}

//1、定义一个实现Callable接口的实现类
class My_Callable implements Callable<String>{
    private int n;
    public My_Callable(int n){
        this.n = n;
    }

    //2、重写call方法,定义线程执行体
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum+=i;
        }
        return Thread.currentThread().getName()+"-"+n+"的和是:"+sum;
    }
}

运行结果:

pool-1-thread-1-100的和是:5050
pool-1-thread-2-200的和是:20100
pool-1-thread-3-300的和是:45150
pool-1-thread-1-400的和是:80200

可见此时线程1被复用。

2、总结

五、通过Executors工具类,创建线程池

1、该工具类中有哪些静态方法?

        举例:

        

public class Test10 {
    public static void main(String[] args) {
        //1、通过线程池工具类:Executors,调用其静态方法直接得到线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);

        //2、使用线程池处理任务!看会不会复用线程?
        Future<String> f1 = pool.submit(new My_Callable(100));
        Future<String> f2 = pool.submit(new My_Callable(200));
        Future<String> f3 = pool.submit(new My_Callable(300));
        Future<String> f4 = pool.submit(new My_Callable(400));

        try {
            System.out.println(f1.get());
            System.out.println(f2.get());
            System.out.println(f3.get());
            System.out.println(f4.get());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        //3、关闭线程池:一般不关闭线程池
        //pool.shutdown();//等所有任务执行完毕后,再关闭线程池!
        //pool.shutdownNow();//立即关闭,不管任务是否执行完毕!
    }
}

//1、定义一个实现Callable接口的实现类
class My_Callable implements Callable<String>{
    private int n;
    public My_Callable(int n){
        this.n = n;
    }

    //2、重写call方法,定义线程执行体
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum+=i;
        }
        return Thread.currentThread().getName()+"-"+n+"的和是:"+sum;
    }
}

运行结果:

pool-1-thread-1-100的和是:5050
pool-1-thread-2-200的和是:20100
pool-1-thread-3-300的和是:45150
pool-1-thread-3-400的和是:80200

2、Executors工具类使用时可能存在的风险

3、总结

六、并发、并行

1、什么是进程?

        正在运行的程序(软件)就是一个进程。

举例

2、什么是线程?

        线程属于进程,一个进程可以同时运行很多个线程。

        进程中的多个线程是并发+并行执行的。 

3、什么是并发?

        并发:进程中的线程是由CPU负责调度执行的,但CPU同时处理线程的数量是有限的(由CPU的核数决定),为了能保证全部线程都能往前执行,因此CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉是这些线程在同时执行,这就是并发。

举例

假设我的CPU是单核的(同一时刻只能运行一个线程),但是会让很多线程轮流切换上CPU执行,切换速度极快,仿佛是同时在运行所有进程中的线程,这就叫“并发”。

4、什么是并行?

        并行:在同一个时刻上,同时有多个线程(数量取决于CPU的核数)在被CPU调度执行。

举例

假设我的CPU是4核,那么同一时刻,会有4个线程同时上cpu执行,这就叫“并行”。

                   说白了,并行的线程数,取决于CPU的核数。

 5、总结

举例

                    通过查看某人电脑的任务管理器,得知该电脑的CPU是20核的,也就是说,同一时刻允许20个线程上CPU执行,那么这20个线程就是并行关系;

                   该电脑目前需要处理6800个线程,那么就会20个一组上CPU轮流切换运行,由于切换速度极快,仿佛是同时在运行,这就叫“并发”。

六、综合案例-抢红包游戏

1、介绍

2、编码

public class Test12 {
    public static void main(String[] args) {
        /**
         * 目标:完成多线程的综合小案例
         * 红包雨游戏:某企业有100名员工,员工的工号依次是1,2,3,4...到100
         * 现在公司举办了年会活动,活动中有一个红包雨环节,要求共计发出200个红包雨,其中小红包在【1 - 30】 元之间
         * 总占比80%,大红包在【31 - 100】元, 总占比20%
         * 分析:100个员工实际上就是100个线程,来竞争200个红包
         */
        List<Integer> redPacket = getRedPacket();
        //2、定义线程类,创建100个线程,竞争同一个集合
        for (int i = 1; i <= 100; i++) {
            new EmployeeGetPacket(redPacket, "员工"+i).start();
        }
    }

    //1、准备这200个随机的红包返回,放到这个List集合中
    public static List<Integer> getRedPacket(){
        Random r = new Random();
        //其中小红包在【1 - 30】 元之间,总占比80%(即160个),大红包在【31 - 100】元,总占比为20%(即40个)
        ArrayList<Integer> redPacket = new ArrayList<>();
        for (int i = 1; i <= 160; i++) {
            redPacket.add(r.nextInt(30) + 1);
        }
        for (int i = 1; i <= 40; i++) {
            redPacket.add(r.nextInt(70) + 31);
        }
        return redPacket;
    }
}

//员工抢红包的线程
class EmployeeGetPacket extends Thread{
    private List<Integer> redPacket;//红包集合

    //构造器
    public EmployeeGetPacket(List<Integer> redPacket, String name) {
        super(name);
        this.redPacket = redPacket;
    }

    public void run() {
        String name = Thread.currentThread().getName();
        while(true){
            //100个人来抢redPackage集合中的钱
            synchronized (redPacket){
                if(redPacket.size() == 0){
                    break;
                }
                //随机一个索引得到红包
                int index = (int)(Math.random() * redPacket.size());
                Integer money = redPacket.remove(index);
                System.out.println(name + "抢到了" + money + "元");
                if(redPacket.size() == 0){
                    System.out.println("活动结束!");
                    break;
                }
            }
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }
        }
    }
}

运行结果:

员工1抢到了3元
员工99抢到了20元
员工98抢到了59元
员工100抢到了14元
员工96抢到了2元
员工97抢到了17元
员工95抢到了20元
员工94抢到了12元
员工93抢到了8元
员工92抢到了26元
员工91抢到了7元
...不全部展示了,因为200条抢红包记录,太长了...

以上就是线程池、并发、并行、抢红包游戏案例的全部内容,想了解更多Java知识,请关注本博主~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2244864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java数据库连接(Java Database Connectivity,JDBC)

1.JDBC介绍 Java数据库连接&#xff08;Java Database Connectivity&#xff0c;JDBC&#xff09;是SUN公司为了简化、统一对数据库的操作&#xff0c;定义的一套Java操作数据库的规范&#xff08;接口&#xff09;。这套接口由数据库厂商去实现&#xff0c;这样&#xff0c;开…

高亮变色显示文本中的关键字

效果 第一步&#xff1a;按如下所示代码创建一个用来高亮显示文本的工具类&#xff1a; public class KeywordUtil {/*** 单个关键字高亮变色* param color 变化的色值* param text 文字* param keyword 文字中的关键字* return*/public static SpannableString highLigh…

2024强化学习的结构化剪枝模型RL-Pruner原理及实践

[2024] RL-Pruner: Structured Pruning Using Reinforcement Learning for CNN Compression and Acceleration 目录 [2024] RL-Pruner: Structured Pruning Using Reinforcement Learning for CNN Compression and Acceleration一、论文说明二、原理三、实验与分析1、环境配置在…

电脑超频是什么意思?超频的好处和坏处

嗨&#xff0c;亲爱的小伙伴&#xff01;你是否曾经听说过电脑超频&#xff1f;在电脑爱好者的圈子里&#xff0c;这个词似乎非常熟悉&#xff0c;但对很多普通用户来说&#xff0c;它可能还是一个神秘而陌生的存在。 今天&#xff0c;我将带你揭开超频的神秘面纱&#xff0c;…

uniapp: vite配置rollup-plugin-visualizer进行小程序依赖可视化分析减少vender.js大小

一、前言 在之前文章《uniapp: 微信小程序包体积超过2M的优化方法&#xff08;主包从2.7M优化到1.5M以内&#xff09;》中&#xff0c;提到了6种优化小程序包体积的方法&#xff0c;但并没有涉及如何分析common/vender.js这个文件的优化&#xff0c;而这个文件的大小通常情况下…

SQL Server Management Studio 的JDBC驱动程序和IDEA 连接

一、数据库准备 &#xff08;一&#xff09;启用 TCP/IP 协议 操作入口 首先&#xff0c;我们要找到 SQL Server 配置管理器&#xff0c;操作路径为&#xff1a;通过 “此电脑” 右键选择 “管理”&#xff0c;在弹出的 “计算机管理” 窗口中&#xff0c;找到 “服务和应用程…

STM32F103系统时钟配置

时钟是单片机运行的基础&#xff0c;时钟信号推动单片机内各个部分执行相应的指令。时钟系统就是CPU的脉搏&#xff0c;决定CPU速率&#xff0c;像人的心跳一样 只有有了心跳&#xff0c;人才能做其他的事情&#xff0c;而单片机有了时钟&#xff0c;才能够运行执行指令&#x…

鸿蒙进阶篇-Math、Date

“在科技的浪潮中&#xff0c;鸿蒙操作系统宛如一颗璀璨的新星&#xff0c;引领着创新的方向。作为鸿蒙开天组&#xff0c;今天我们将一同踏上鸿蒙基础的探索之旅&#xff0c;为您揭开这一神奇系统的神秘面纱。” 各位小伙伴们我们又见面了,我就是鸿蒙开天组,下面让我们进入今…

RAID存储技术 详解

RAID&#xff08;Redundant Array of Independent Disks&#xff0c;独立磁盘冗余阵列&#xff09;是一种将多个物理硬盘组合为一个逻辑存储单元的技术。它通过分布数据、冗余校验和容错能力&#xff0c;提高存储系统的性能、可靠性和容量利用率。 以下从底层原理和源代码层面…

MTK主板定制_联发科主板_MTK8766/MTK8768/MTK8788安卓主板方案

主流市场上的MTK主板通常采用联发科的多种芯片平台&#xff0c;如MT8766、MT6765、MT6762、MT8768和MT8788等。这些芯片基于64位Cortex-A73/A53架构&#xff0c;提供四核或八核配置&#xff0c;主频可达2.1GHz&#xff0c;赋予设备卓越的计算与处理能力。芯片采用12纳米制程工艺…

免费微调自己的大模型(llama-factory微调llama3.1-8b)

目录 1. 名词/工具解释2. 微调过程3. 总结 本文主要介绍通过llama-factory框架&#xff0c;使用Lora微调方法&#xff0c;微调meta开源的llama3.1-8b模型&#xff0c;平台使用的是趋动云GPU算力资源。 微调已经经过预训练的大模型目的是&#xff0c;通过调整模型参数和不断优化…

MySQL 中 InnoDB 支持的四种事务隔离级别名称,以及逐级之间的区别?

MySQL中的InnoDB存储引擎支持四种事务隔离级别&#xff0c;这些级别定义了事务在并发环境中的行为和相互之间的可见性。以下是这四种隔离级别的名称以及它们之间的区别&#xff1a; 读未提交&#xff08;Read Uncommitted&#xff09; 特点&#xff1a;这是最低的隔离级别&…

【YOLOv10改进[注意力]】引入并行分块注意力PPA(2024.3.16) + 适于微小目标

本文将进行在YOLOv10中引入并行分块注意力PPA魔改v10 的实践,文中含全部代码、详细修改方式。助您轻松理解改进的方法。 一 HCF 论文题目:Hierarchica

共建智能软件开发联合实验室,怿星科技助力东风柳汽加速智能化技术创新

11月14日&#xff0c;以“奋进70载&#xff0c;智创新纪元”为主题的2024东风柳汽第二届科技周在柳州盛大开幕&#xff0c;吸引了来自全国的汽车行业嘉宾、技术专家齐聚一堂&#xff0c;共襄盛举&#xff0c;一同探寻如何凭借 “新技术、新实力” 这一关键契机&#xff0c;为新…

在ubuntu下,使用Python画图,无法显示中文怎么解决

1.首先需要下载中文字体&#xff0c;推荐simsun&#xff0c;即宋体&#xff0c;地址如下 https://www.freefonts.io/download/simsun/ 2.下载完要把字体文件放进字体目录&#xff0c;具体方法如下&#xff1b; a.创建字体目录&#xff1a;sudo mkdir -p /usr/share/fonts/truet…

鸿蒙实战:使用显式Want启动Ability

文章目录 1. 实战概述2. 实现步骤2.1 创建鸿蒙应用项目2.2 修改Index.ets代码2.3 创建SecondAbility2.4 创建Second.ets 3. 测试效果4. 实战总结5. 拓展练习 - 启动文件管理器5.1 创建鸿蒙应用项目5.2 修改Index.ets代码5.3 测试应用运行效果 1. 实战概述 本实战详细阐述了在 …

《Python浪漫的烟花表白特效》

一、背景介绍 烟花象征着浪漫与激情&#xff0c;将它与表白结合在一起&#xff0c;会创造出别具一格的惊喜效果。使用Python的turtle模块&#xff0c;我们可以轻松绘制出动态的烟花特效&#xff0c;再配合文字表白&#xff0c;打造一段专属的浪漫体验。 接下来&#xff0c;让…

springboot中设计基于Redisson的分布式锁注解

如何使用AOP设计一个分布式锁注解&#xff1f; 1、在pom.xml中配置依赖 <dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.26</version></dependency><dependenc…

绕过CDN寻找真实IP

在新型涉网案件中&#xff0c;我们在搜集到目标主站之后常常需要获取对方网站的真实IP去进一步的信息搜集&#xff0c;但是现在网站大多都部署了CDN&#xff0c;将资源部署分发到边缘服务器&#xff0c;实现均衡负载&#xff0c;降低网络堵塞&#xff0c;让用户能够更快地访问自…

【Redis】redis缓存击穿,缓存雪崩,缓存穿透

一、什么是缓存&#xff1f; 缓存就是与数据交互中的缓冲区&#xff0c;它一般存储在内存中且读写效率高&#xff0c;提高响应时间提高并发性能&#xff0c;如果访问数据的话可以先访问缓存&#xff0c;避免数据查询直接操作数据库&#xff0c;造成后端压力过大。 但是可能会面…