四大经典案例_线程池及java代码实现

举报
bug郭 发表于 2022/09/30 23:39:20 2022/09/30
【摘要】 大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流作者简介:CSDN java领域新星创作者blog.csdn.net/bug…掘金LV3用户 juejin.cn/user/bug…阿里云社区专家博主,星级博主,developer.a...

大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流

作者简介:

线程池

我们之前学过常量池!这里的线程池也大同小异!
我们通过创建很多个线程放在一块空间不进行销毁,等到需要的时候就启动线程!避免了创建销毁的时间开销! 提高开发效率!
我们之前不是说一个线程创建并不会划分很多时间吗! 但是我们的多线程编程,有时候需要使用到很多很多线程,如果要进行创建,效率就不高,而线程池或者协程(我们后面会介绍)就避免了创建销毁线程! 但我们需要用到线程时,自己从线程池中给出就好!

我们创建线程的本质还是要通过内核态(就是我们的操作系统)进行创建,然而内核态创建的时间,我们程序员无法掌控,而通过线程池,我们就可以避免了内核态的操作,直接在用户态,进行线程的调用,也就是应用程序层!
在这里插入图片描述
使用线程池大大提高了我们的开发效率!

我们来学习一下java中给我们提供的线程池类,然后自己实现一个线程池!

ThreadPoolExecutor 线程池

这个类在java.util.concurrent并发编程包下,我们用到的很多关于并发编程的类都在!

可以看到这个线程池有4个构造方法!

在这里插入图片描述我们了解一下参数最多的那个方法!

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

创建一个新的 ThreadPoolExecutor与给定的初始参数。

参数
    corePoolSize - 即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut 
    maximumPoolSize - 池中允许的最大线程数 
    keepAliveTime - 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间。 
    unit - keepAliveTime参数的时间单位 
    workQueue - 用于在执行任务之前使用的队列。 这个队列将仅保存execute方法提交的Runnable任务。 
    threadFactory - 执行程序创建新线程时使用的工厂 
    handler - 执行被阻止时使用的处理程序,因为达到线程限制和队列容量 

我们这里的线程池类比一个公司,便于我们理解该类

  • int maximumPoolSize,
    核心线程数(正式员工)
  • maximumPoolSize
    池中允许的最大线程数(正式员工+临时工)
  • long keepAliveTime,
    多余的空闲线程的允许等待的最大时间(临时工摸鱼时间)
  • TimeUnit unit,
    时间单位
    -BlockingQueue<Runnable> workQueue,
    任务队列,该类中用一个submit方法,用于将任务注册到线程池,加入到任务队列中!
  • ThreadFactory threadFactory,
    线程工厂,线程是如何创建的
  • RejectedExecutionHandler handler
    拒绝策略
    但任务队列满了后怎么做
    1.阻塞等待,
    2.丢弃久任务
    3.忽略新任务

可以看到java给我们提供的这个线程池类让人头大!
但是不必焦虑,我们只需要知道int maximumPoolSize,
核心线程数和 maximumPoolSize 池中允许的最大线程数即可!
面试问题
思考一个问题
我们有一个程序需要多线程并发处理一些任务,使用线程池的话,需要设置多大的线程数?
这里的话,我们无法准确的给出一个数值,我们要通过性能测试的方式找个一个平衡点!

例如我们写一个服务器程序:服务器通过线程池多线程处理机用户请求!如果要确定线程池的线程数的话,就需要通过对该服务器进行性能分析,构造很多很多请求模拟真实环境,根据这里不同的线程数,来观察处理任务的速度和当个线程的cpu占用率!从而找到一个平衡点!
如果cpu暂用率过高,就无法应对一些突发情况,服务器容易挂!

我们java根据上面的ThreadPoolExecutor类进行封装提供了一个简化版本的线程池!Executors供我们使用!
我们通过Executors的使用学习,实现一个线程池!

Executors

java.util.concurrent.Executors

下面都是Executor类中创建线程池的一些静态方法

创建可以扩容的线程池

在这里插入图片描述创建一个指定容量的线程池
在这里插入图片描述创建单线程池
在这里插入图片描述创建一个线程池含有任务队列
在这里插入图片描述
我们重点学习创建指定大小得到线程池方法!

//Executors使用案例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo3 {
    public static void main(String[] args) {
        //创建一个指定线程个数为10的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            int finalI = i;
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello executor!"+ finalI);
                }
            });
        }
    }
}

在这里插入图片描述
我们通过ExecutorService类中的submit可以将多个任务注册到线程池中,然后线程池中的线程将任务并发执行,大大提升了编程效率!可以看到,啪的一下,100个任务给10个线程一下就执行结束了!

实现线程池

我们还是分析一下线程池用什么功能,里面都有些啥!

  • 能够描述任务(直接用runnable)
  • 需要组织任务(使用BlockingQueue)
  • 能够描述工作线程
  • 组织线程
  • 需要实现往线程池里添加任务
//模拟实现线程池
class ThreadPool {
    //描述任务 直接使用Runnable
    //组织任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingDeque<>();

    //描述工作线程
    static class Worker extends Thread {//继承Thread类
        BlockingQueue<Runnable> queue = null;
        @Override
        public void run() {
            while (true){
                try {
                    //拿到任务
                    Runnable runnable = queue.take();
                    //执行任务
                    runnable.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        //通过构造方法拿到外面的任务队列!
        public Worker(BlockingQueue<Runnable> queue) {
            this.queue = queue;
        }
    }

    //组织多个工作线程
    //将多个工作线程放入到workers中!
    public List<Thread>workers = new LinkedList<>();

    public ThreadPool(int n) {//指定放入线程数量
        for (int i = 0; i < n; i++) {//创建多个工作线程
            Worker worker = new Worker(queue);
            worker.start();//启动工作线程
            workers.add(worker);//放入线程池
        }
    }
    //创建一个方法供我们放入任务
    public void submit(Runnable runnable){
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//测试代码
public class demo5 {
    public static void main(String[] args) {
        //线程池线程数量10
        ThreadPool pool = new ThreadPool(10);
        for (int i = 0; i <100 ; i++) {//100个任务
            int finalI = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello ThreadPool "+ finalI);
                }
            });
        }
    }
}

运行效果
在这里插入图片描述

案例总结

  • 线程安全单例模式
  • 阻塞队列->生产着消费者模型
  • 定时器
  1. MyTask类描述一个任务 Runnable + time
  2. 带有优先级的阻塞队列
  3. 扫描线程,不停从队首取出元素,检测时间是否到达,并且执行任务,使用wait解决忙等位问题!
  4. 实现schedule方法
  • 线程池
  1. 描述一个任务Runnable
  2. 组织任务,带有优先级的阻塞队列
  3. 创建一个工作线程work类,从任务队列获取任务,执行任务
  4. 组织工作线程works数据结构存放work
  5. 实现一个submit方法将任务放入任务队列中!
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。