线程池(内置线程池ThreadPoolExecutor、自定义线程池)

目录

线程池诞生背景

为什么线程池比线程效率高?

java标准库中,有线程池的具体实现

java内置线程池ThreadPoolExecutor

拒绝策略:

自己实现线程池


线程池诞生背景

线程诞生:

进程创建销毁太重量了

线程池诞生:

线程频繁创建销毁太重量了

提高线程频繁创建、销毁效率:

1.协程(轻量级线程)

相比于线程,省略系统调度(程序猿手动调度)

java标准库没有协程,第三方库中实现了协程

(java中不咋用 这里不过多讨论)

2.线程池

在使用第一个线程的时候,把2 3 4...个线程创建好,要使用的时候不必创建了,直接使用(降低了线程创建的开销)

线程池最大的好处就是减少每次启动、销毁线程的损耗

为什么线程池比线程效率高?

创建线程:由用户态+内核态共同完成

从线程池中取:由用户态完成

(由内核态操作时 内核难免要做一些其他的事 不可控)

java标准库中,有线程池的具体实现

1.

ExecutorService service= Executors.newCachedThreadPool();

 Executors为工厂类,newCachedThreadPool()为工厂方法

此时构造出的线程池对象中的线程数目能动态适应,随着往线程池中添加任务,线程池中的线程会根据需要自动创建出来,创建出来之后也不会着急销毁,会在池子里保留一定时间以备随时再使用

2.

创建有固定数量线程的线程池

    ExecutorService service2=Executors.newFixedThreadPool(5);

3.

创建只有一个线程的线程池

    ExecutorService service3=Executors.newSingleThreadExecutor();

4.

有多个线程执行时间到的任务

ExecutorService service4=Executors.newScheduledThreadPool(5);

掌握1、2种 

线程池不是通过new触发类的构造方法

而是使用工厂模式

使用构造方法存在一定局限性,当一个类需要多个构造方法时,只能通过重载的方式(参数个数/类型不同),不能完成需要实现有多个参数个数、类型相同的构造方法

工厂模式通过额外定义一个类,在类中定义不同的静态方法构造对象

//创建一个Point的工厂类
class PointFactory{
    public static Point makePointByXY(double x,double y){
        Point p=new Point();
        p.setX(x);
        p.setY(y);
    }
    public static Point makePointByRA(double r,double a){
        Point p=new Point();
        p.setR(r);
        p.setA(a);
    }
}

java内置线程池ThreadPoolExecutor

(上述工厂方法 本质上是对ThreadPoolExecutor的封装)

①int corePoolSize核心线程数

②int maximumPoolSize最大线程数

线程池中的线程数量[corePoolSize,maximumPoolSize]

③keepAliveTime 存活时间

④unit 存活时间单位

⑤BlockingQueue <Runnable> workQueue阻塞队列,用来存放线程池中的任务

(队列可以灵活设置

需要优先级->PriorityBlockingQueue

不需要优先级,任务数量相对恒定->ArrayBlockingQueue

不需要优先级,任务数量变动较大->LinkedBlockingQueue

⑥threadFactory工厂模式的体现

⑦handler线程池的拒绝策略,当线程池中数量已经满了,再添加线程时,不同的拒绝策略产生不同效果

拒绝策略:

重点掌握线程数目、拒绝策略 面试必考!!

使用线程池中线程的数目设置为多少合适?

线程执行的代码主要有两类:

①cpu密集型:代码里的逻辑是在算术运算/逻辑判断

②io密集型:代码里主要进行io操作

cpu逻辑核心数为N

线程为cpu密集型时,线程池中线程数目应不超过N,如果比N更大,也无法提高效率了(cpu吃满了),反而会增加调度的开销

线程为io密集型时,这个时候不吃cpu,线程数可以是超过n较大的值(一个cpu核心调度并发执行多个线程)

无法知道一个代码多少内容是cpu密集,多少内容是io密集

所以,正确做法是:

通过实验的方式,对程序进行性能测试,修改不同的线程数目,看哪种情况下最适合

(网上普遍流传的N N-1 N+2 1.5N....都不科学)

自己实现线程池

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

//自定义线程池
class MyThreadPool{
    //任务队列
    private BlockingQueue<Runnable> queue=new ArrayBlockingQueue<>(10);
    //添加任务到任务队列中
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
    public MyThreadPool(int n){
        //创建出n个线程,负责执行上述队列中的任务
        for(int i=0;i<n;i++){
            Thread t=new Thread(()->{
                Runnable runnable= null;
                try {
                    runnable = queue.take();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                runnable.run();
            });
            t.start();
        }
    }
}
public class demo3 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool threadPool=new MyThreadPool(4);
        for(int i=0;i<4;i++){
            //此处每次循环创建一个新的不变的量 目的是防止匿名内部类编译报错
            int id=i;
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(id);
            }
        });
        }
    }
}

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值