提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、什么是线程池
- 二.为什么要有线程池
- 引入:
- 为什么从池子里取,比创建线程速度要快
- 什么是用户态,什么是内核态
- 最终结论:
 
 
- 三.标准库中的线程池
- 四.自己实现一个线程池
- 生产者消费者模型策略:
- 代码演示:
- 完整代码:
 
- 五.线程池的拒绝策略 (超级重点):
- 引入
- 标准库中拒绝策略(对于threadpoolExecutr的理解)
- 延伸的问题:
- 实际开发中线程池数目如何确定:
 
 
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是线程池
类似String字符串常量池,mysql jdbc数据库链接池
 搞一个池子,创建好许多线程,当需要执行任务的时候,不需要在重新创建线程,而是直接从池子里取一个现成的线程,直接使用,用完了也不释放,而是直接放回线程池里
二.为什么要有线程池
引入:
线程诞生的目的是进程太重量了,创建进程或销毁进程,都是比较低效(内存资源的申请和释放),线程就是共享了内存资源,新的线程复用之前的资源,不必重新申请,但是如果线程创建的速率进一步频繁,此时线程创建开销仍然不能忽略此时可以采用线程池来优化速度
为什么从池子里取,比创建线程速度要快
实际上,创建线程也需要申请资源(很少),创建线程,也是要在操作系统内核中完成,涉及到用户态->内核态切换操作,也存在一定的开销
什么是用户态,什么是内核态

 应用程序发起的一个创建线程的行为,本质上是pcb,是内核中的数据结构,
 应用程序需要通过系统调用,进入到操作系统内核中执行,
 内核完成pcb的创建,把pcb加入到调度队列,再返回给应用程序
从线程池取线程,把线程放回线程池,这是纯用户态的逻辑
 从系统这里创建线程,是用户态+内核态共同完成的逻辑
最终结论:
使用线程池是纯用户态的操作,要比创建线程(经历内核态的操作)要快**
三.标准库中的线程池
  ExecutorService pool= Executors.newCachedThreadPool();
        pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是任务");
            }
        });
0.标准库中线程池构造方法的参数
 
 
1.如果直接采取构造方法创建线程池(参数太多)写起来麻烦,为了简化构造,标准库提供了一系列工厂方法
2.此处创建线程池没有显示new,而是通过executor类的静态方法newCachedThreadpool来完成
3.工厂模式的好处:
 绕开构造方法的局限(参数太多),而且构造方法有时候不能重载(返回值,参数列表都相同)
 
我们可以使用普通方法代替构造方法(普通方法方法名可以随便取来重载),在普通方法创建point对象,通过其他手段设置
 public static Point makePointByXY(double x,double y){
        Point p=new Point();
        p.setR(x);
        p.setA(y);
        return p;
    }
    private void setA(double a){
    }
    private void setR(double r){
    }
   
线程池的使用采取submit方法,把任务提交到线程池中即可,线程池里面有线程完成这里的任务
四.自己实现一个线程池
一个线程池可以同时提交n个任务,对应的线程池就有m个线程来负责完成这n个任务
生产者消费者模型策略:
先搞一个阻塞队列,每个被提交的任务都被放到阻塞队列当中,
 搞m个线程来取队列元素,如果队列空了,m个线程自然阻塞等待,
 队列不为空,每个线程都取任务,执行任务,再取下一个任务,直到队列为空
代码演示:
  public MyThreadPool(int m){
        //在构造方法中,创建出m个线程,负责完成工作
        for (int i = 0; i <m ; i++) {
            Thread t=new Thread(()->{
                while(true){
                    try {
                        Runnable runnable=queue.take();
                        runnable.run();
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }
创建m个线程,让线程持续的扫描队列,执行其中方法
完整代码:
package Threading;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
//演示自己创建的线程池
class MyThreadPool{
    private BlockingQueue<Runnable> queue=new LinkedBlockingDeque<>();
    public void submit(Runnable runnable) throws InterruptedException{
        queue.put(runnable);
    }
    public MyThreadPool(int m){
        //在构造方法中,创建出m个线程,负责完成工作
        for (int i = 0; i <m ; i++) {
            Thread t=new Thread(()->{
                while(true){
                    try {
                        Runnable runnable=queue.take();
                        runnable.run();
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }
}
public class demo28 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool=new MyThreadPool(10);
        for (int i = 0; i <1000 ; i++) {
            int taskId=i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行当前任务: "+taskId+"当前线程: "+Thread.currentThread().getName());
                }
            });
        }
    }
}
五.线程池的拒绝策略 (超级重点):
引入
线程池的拒绝策略,线程池的任务队列已经满了(工作线程已经忙不过来了)
 当前任务数>workqueue.size()+maximumpoolsize
 如果又有人往里添加新的任务,问题:
 这个策略对于高并发服务器,也是非常有意义的
标准库中拒绝策略(对于threadpoolExecutr的理解)

 AbortPolicy 直接丢弃任务,抛出异常,必须处理好异常,否则打断当前执行流程默认策略
 CallerrunsPolicy 只用调用者所在的线程来处理任务
 discradoldesrPolict 丢弃等待队列中最旧的任务,并执行当前任务
 DiscardPolicy 直接丢器,啥事没有
延伸的问题:
实际开发中线程池数目如何确定:
实践是检验真理的唯一标准
 针对你的程序进行性能测试,分别给线程设置成不同的数目,分别记录每种情况下,你的程序一些核心性能制表和系统负载情况,选一个最合适的



















