面向对象基础篇 – 多线程编程 – 线程池

面向对象基础篇 – 多线程编程 – 线程池

  • Java线程池是1.5 提供juc包中的,底层实现其实就是Callable 和 Future 接口
  • 根据情况,我们可以创建很多种不同场景的线程池

Executors.newSingleThreadExecutor()

  • 单例线程池
  • 只有一个线程,固定不会变化
    @Test
    void test01(){
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName());
            });
        }
    }
/*
执行结果
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
*/

Executors.newFixedThreadPool(5)

  • 固定大小的线程池
  • 使用场景:并发量不会发生变化(并发量的变化很小)
    @Test
    void test02(){
        //开辟5个线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            final  int  index = i ;
            executorService.submit(()-> System.out.println(Thread.currentThread().getName()+"-->"+index));
        }
    }
/*
执行输出
pool-1-thread-1-->0
pool-1-thread-2-->1
pool-1-thread-5-->4
pool-1-thread-4-->3
pool-1-thread-4-->6
pool-1-thread-4-->7
pool-1-thread-4-->8
pool-1-thread-3-->2
pool-1-thread-4-->9
pool-1-thread-1-->5
*/

Executors.newCachedThreadPool()

  • 可缓存的线程池
  • 若线程超过处理所需,缓存一段时间后会回收
  • 若线程不够,则新建线程
  • 并发量变化比较明显的可以用这个
@Test
    void test03(){
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            final int index = i ;
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"-->"+index);
            });
        }
    }
/*
执行输出
pool-1-thread-3-->2
pool-1-thread-20-->19
pool-1-thread-14-->13
pool-1-thread-18-->17
pool-1-thread-7-->6
pool-1-thread-12-->11
pool-1-thread-16-->15
pool-1-thread-17-->16
pool-1-thread-15-->14
pool-1-thread-1-->0
pool-1-thread-2-->1
pool-1-thread-13-->12
pool-1-thread-9-->8
pool-1-thread-4-->3
pool-1-thread-6-->5
pool-1-thread-19-->18
pool-1-thread-5-->4
pool-1-thread-8-->7
pool-1-thread-11-->10
pool-1-thread-10-->9
*/

Executors.newScheduledThreadPool()

  • 让线程延时执行
    @Test
    void test04(){
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        for (int i = 0; i < 10; i++) {
            final  int  index = i ;
            //参数一,要执行的任务
            //参数二,延时多久
            //参数三,延时单位
            scheduledExecutorService.schedule(()-> System.out.println(Thread.currentThread().getName()+"-->"+index),5, TimeUnit.SECONDS);
        }
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
/*
执行结果
pool-1-thread-10-->9
pool-1-thread-8-->7
pool-1-thread-3-->2
pool-1-thread-6-->5
pool-1-thread-2-->1
pool-1-thread-1-->0
pool-1-thread-5-->4
pool-1-thread-9-->8
pool-1-thread-4-->3
pool-1-thread-7-->6
*/

Executors.newSingleThreadScheduledExecutor()

  • 单例的延时线程池
  • 将上一个创建延时线程数量设为1,也可以达到这个目的
    @Test
    void test05(){
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        System.out.println("添加任务,时间:"+new Date());
        scheduledExecutorService.schedule(()->{
            System.out.println("线程被执行时间 :"+new Date());
        },2,TimeUnit.SECONDS);
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
/* 
执行结果
添加任务,时间:Wed Nov 16 15:38:55 CST 2022
线程被执行时间 :Wed Nov 16 15:38:57 CST 2022
*/

Executors.newWorkStealingPool()

  • jdk1.8之后才提供
  • 是抢占式的线程池
  • 也就是任务顺序是随机的

ThreadPoolExecutor

  • 最原始的创建线程池的方式,包含了七个参数

  • 阿里巴巴java开发手册中规定,线程池必须使用这个来创建

  • 构造函数如下

public ThreadPoolExecutor(int corePoolSize,//线程数
                              int maximumPoolSize,//最大线程数,因为它也是可缓存的
                              long keepAliveTime,//缓存时间
                              TimeUnit unit,//缓存时间单位
                              BlockingQueue<Runnable> workQueue,//任务队列
                              ThreadFactory threadFactory,//线程工厂,用于创建线程,一般用默认即可;
                              RejectedExecutionHandler handler//拒绝策略;当任务太多来不及处理时,如何拒绝任务;
                         ) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • 其中前五个参数尤为重要
//示例    
@Test
    void test06(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(500,1000,60,TimeUnit.SECONDS,new LinkedBlockingDeque<>());

        //建议使用自己构建的线程池
        
        /*
        执行线程
        threadPoolExecutor.execute();
         */

    }

一、任务队列 workQueue

1、直接提交队列:设置为SynchronousQueue队列,SynchronousQueue是一个特殊的BlockingQueue,它没有容量,每执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的进程,如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执行拒绝策略;

2、有界的任务队列:有界的任务队列可以使用ArrayBlockingQueue实现。使ArrayBlockingQueue有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。

3、无界的任务队列:有界任务队列可以使用LinkedBlockingQueue实现。使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题。

4**、优先任务队列:**优先任务队列通过PriorityBlockingQueue实现。它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

二、拒绝策略

**1、AbortPolicy策略:**该策略会直接抛出异常,阻止系统正常工作;(默认策略)

**2、CallerRunsPolicy策略:**如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;

**3、DiscardOledestPolicy策略:**该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;

**4、DiscardPolicy策略:**该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值