并发编程(五):Executor框架

简介

Executor是JDK提供的一套线程框架,用于有效的控制线程。

Executor框架主要包含三个部分:

任务:包括Runnable和Callable,其中Runnable表示一个可以异步执行的任务,而Callable表示一个会产生结果的任务

任务的执行:包括Executor框架的核心接口Executor以及其子接口ExecutorService。在Executor框架中有两个关键类ThreadPoolExecutor和ScheduledThreadPoolExecutor实现了ExecutorService接口。

异步计算的结果:包括接口Future和其实现类FutureTask。

Executors创建线程池的方法

Executors框架可以创建几个固定的线程池:

  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

  3. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

但是有时候这些线程池无法满足我们的需求,这时候就是需要自定义线程池了,这就需要了解ThreadPoolExecutor的各个参数:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) //后两个参数为可选参数

参数说明:

corePoolSize:核心线程数,如果运行的线程少于corePoolSize,则创建新线程来执行新任务,即使线程池中的其他线程是空闲的

maximumPoolSize:最大线程数,可允许创建的线程数,corePoolSize和maximumPoolSize设置的边界自动调整池大小:

corePoolSize <运行的线程数< maximumPoolSize:仅当队列满时才创建新线程

corePoolSize=运行的线程数= maximumPoolSize:创建固定大小的线程池

keepAliveTime:如果线程数多于corePoolSize,则这些多余的线程的空闲时间超过keepAliveTime时将被终止。

unit:keepAliveTime参数的时间单位

workQueue:保存任务的阻塞队列,与线程池的大小有关:当运行的线程数少于corePoolSize时,在有新任务时直接创建新线程来执行任务而无需再进队列,当运行的线程数等于或多于corePoolSize,在有新任务添加时则选加入队列,不直接创建线程当队列满时,在有新任务时就创建新线程

threadFactory:使用ThreadFactory创建新线程,默认使用defaultThreadFactory创建线程

handle:定义处理被拒绝任务的策略,默认使用ThreadPoolExecutor.AbortPolicy,任务被拒绝时将抛出RejectExecutorException

例如:

package com.tgb;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class UseThreadPoolExecutor2 implements Runnable{

    private static AtomicInteger count = new AtomicInteger(0);

    @Override
    public void run() {
        try {
            int temp = count.incrementAndGet();
            System.out.println("任务" + temp);
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception{
        //无界队列
        //BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
        //有界队列
        BlockingQueue<Runnable> queue =
                new ArrayBlockingQueue<Runnable>(10);
        ExecutorService executor  = new ThreadPoolExecutor(
                    5,      //core
                    10,     //max
                    120L,   //2fenzhong
                    TimeUnit.SECONDS,
                    queue);

        for(int i = 0 ; i < 20; i++){
            executor.execute(new UseThreadPoolExecutor2());
        }
        Thread.sleep(1000);
        System.out.println("queue size:" + queue.size());       //10
        Thread.sleep(2000);
    }


}

有界/无界队列

在使用有界队列时: 若有新的任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,若大于corPoolSiz,则会将任务加入队列,若队列已满,则在总线程数不大于maxmumPoolsize的前提下,创建新的线程,若线程数大于maximumPoolSize,则执行拒绝策略,或者其他自定义方式。

这里写图片描述

核心线程数同生同死;

A+B < C

无界的任务队列:LinkedBlockingQueuee。与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况,当有新任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务;当达到corePoolSize后,就不会继续增加。若后续仍有新的任务加入,则有没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。

这里写图片描述

Ps:当无界队列限定了数量时,就充当了有界队列的角色;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值