标准的线程池

本文详细介绍了Java线程池的创建,包括核心线程数、最大线程数、存活时间等参数。线程池的任务调度流程涉及核心线程、阻塞队列和拒绝策略。提交任务的execute()和submit()方法的区别在于参数类型和返回值。文章还讨论了调度器的钩子方法,线程池的5种状态及其转换,并提供了优雅关闭线程池的示例。最后,探讨了如何根据任务类型确定线程池的线程数。
摘要由CSDN通过智能技术生成

目录

一、创建方式

二、线程池的任务调度流程

三、提交任务的两种方式

四、调度器的钩子方法

五、线程池的5种状态源码:

六、优雅的关闭线程池

七、确定线程池的线程数


一、创建方式

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
corePoolSize:核心线程数,即使线程空闲也不会被回收。
maximumPoolSize:最大线程数。最大线程数为Integer.MAX_VALUE
keepAliveTime:空闲线程的最大存活时间。
unit:线程存活的时间单位。
时间单位
NANOSECONDS纳秒
MICROSECONDS微秒
MILLISECONDS毫秒
SECONDS
MINUTES
HOURS
DAYS
workQueue:任务阻塞队列。

ArrayBlockingQueue:数组实现的有界阻塞队列,队中元素按FIFO排序,ArrayBlockingQueue在创建时必须指定其大小。

LinkedBlockingQueue:链表实现的阻塞队列,队中元素按FIFO排序。如果不设置LinkBlockingQueue的容量,则默认为Integer.MAX_VALUE,为无界队列。

PriorityBlockingQueue:具有优先级的无界队列。

DelayQueue:无界延迟阻塞队列,队中的每个元素都有过期时间,只有过期的元素才会出队。

SynchronousQueue:(同步队列)不存储元素的阻塞队列,不保存提交的任务,直接创建线程去执行新任务。

threadFactory:创建线程的工厂,制定线程的创建方式。当没有指定时会使用Executors.defaultThreadFactory()的默认实例。
handler:拒绝策略。

AbortPolicy:线程池默认的拒绝策略,如果线程池满,新任务会被拒绝,抛出RejectedExecutionException。

DiscardPolicy:如果线程池满,新任务会被丢掉,且不抛异常。

DiscardOldestPolicy:如果线程池满,会从最早进入队列的任务开始抛弃。

CallerRunsPolicy:如果线程池满,提交任务的线程自己去执行该任务,不会使用线程池中的线程去执行。

二、线程池的任务调度流程

 a、当前工作线程数小于核心线程数时,即使其他工作线程处于空闲状态也会创建新的线程,直到线程数达到核心线程数。

b、在核心线程已经用完,但阻塞队列没有满的情况下,对于新接受的任务不会创建新线程,而是会被添加到阻塞队列中。

c、当一个任务执行结束时,执行器从阻塞队列中获取下一个任务开始执行,直到阻塞对列中的任务被取光。

d、当核心线程和阻塞对列均满的情况下,接受到新任务时会为新任务创建一个线程并立即执行。

e、当核心线程和阻塞对列均满且也已经达到了最大的线程数时,线程池会对新任务执行拒绝策略。

三、提交任务的两种方式

execute()

submit()

区别:

(1)、execute只接受Runnable类型参数,submit可以接受Callable,Runnable类型的参数。

(2)、submit有返回值,而execute没有

(3)、submit可以进行异常捕获,而execute不行。

例子:通过submit()返回的Future对象获取返回结果

package threadPackage;

import java.util.concurrent.*;

public class FutureBack {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(1,
                1,
                1L,
                TimeUnit.SECONDS,
                new SynchronousQueue(),
                Executors.defaultThreadFactory());
        Future<Integer> submit = pool.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 100;
            }
        });
        System.out.println(submit.get());
        pool.shutdown();
    }
}

结果:

100

例子:submit()返回的Future对象捕获异常

package threadPackage;

import java.util.concurrent.*;

public class FutureBack {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(1,
                1,
                1L,
                TimeUnit.SECONDS,
                new SynchronousQueue(),
                Executors.defaultThreadFactory());
        Future<Integer> submit = pool.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                throw new RuntimeException("ERROR");
            }
        });
        try {
            Integer integer = submit.get();
            System.out.println(integer);
        }catch (Exception re){
            System.out.println(re.getMessage());
        }
        pool.shutdown();
    }
}

结果:

java.lang.RuntimeException: ERROR

四、调度器的钩子方法

package threadPackage;

import java.util.concurrent.*;

public class Pool extends ThreadPoolExecutor{

    public Pool() {
        super(5, 5, 0L, TimeUnit.SECONDS, new SynchronousQueue<>());
    }

    // 线程执行前运行
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        System.out.println("beforeExecute doing: " + Thread.currentThread().getName());
        super.beforeExecute(t, r);
    }

    // 线程执行后
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println("afterExecute doing: " + Thread.currentThread().getName());
        super.afterExecute(r, t);
    }

    // 调度器终止后
    @Override
    protected void terminated() {
        System.out.println("terminated doing: " + Thread.currentThread().getName());
        super.terminated();
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Pool pool = new Pool();
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + " doing");
                }
            });
        Thread.sleep(1000);
        pool.shutdown();
    }
}

结果:

beforeExecute doing: pool-1-thread-1
pool-1-thread-1 doing
afterExecute doing: pool-1-thread-1
terminated doing: pool-1-thread-1

可以在线程执行前后获取当前系统时间,计算线程执行时间。

System.currentTimeMillis()

五、线程池的5种状态
源码:

public class ThreadPoolExecutor extends AbstractExecutorService {
 
    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
}

RUNNING:线程池创建的初始状态,该状态下可以执行任务。

SHUTDOWN:该状态下线程池不在接受新任务,但会将工作队列中的任务执行完成。

STOP:该状态下线程池不在接受新任务,也不处理工作队列中的任务,会中断所有的工作线程。 TIDYING:该状态下所有的任务都已终止或者执行完成,将执行terminated()方法。

TERMINATED:执行terminated()方法后的状态。 线程池状态之间的转换图 shutdown():此方法会等待队列中的任务执行完成后才执行关闭。

shutdownNow():立即关闭线程池,中断正在执行的线程,清空队列中的任务,返回尚未执行的任务。

awaitTermination():调用shutdown()或者shutdownNow()方法时,当前线程会立即返回,而awaitTermination()会等待线程池完成关闭。

六、优雅的关闭线程池

package threadPackage;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadUtil {
    
    public static void shutdownThreadPoolGracefully(ExecutorService pool){
        // 若线程池已关闭则返回
        if(pool.isTerminated()){
            return;
        }
        try {
            // 循环等待1000次,每次等待10毫秒
            for (int i=0; i< 1000;i++){
                if (!pool.awaitTermination(10, TimeUnit.MILLISECONDS)) {
                    break;
                }
                // 立即关闭
                pool.shutdownNow();
            }
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
    }
}

七、确定线程池的线程数

a、IO密集型任务:此类任务主要是执行IO操作。

需要开CPU核心数2倍的线程。

package threadPackage;

import java.util.concurrent.*;

public class ThreadUtil {
    // CPU核数
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    // IO处理线程数
    public static final int IO_MAX = Math.max(2, CPU_COUNT * 2);

    // 存活时间,秒
    public static final int KEEP_ALIVE_SECONDS = 30;

    // 有界队列的size
    public static final int QUEUE_SIZE = 128;

    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
            IO_MAX,
            IO_MAX,
            KEEP_ALIVE_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(QUEUE_SIZE),
            Executors.defaultThreadFactory()
    );
}

b、CPU密集型任务:此类任务主要是执行计算任务。

线程数等于cpu核心数

package threadPackage;

import java.util.concurrent.*;

public class ThreadUtil {
    // CPU核数
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    // IO处理线程数
    public static final int POOL_SIZE = CPU_COUNT;

    // 存活时间,秒
    public static final int KEEP_ALIVE_SECONDS = 30;

    // 有界队列的size
    public static final int QUEUE_SIZE = 128;

    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
            POOL_SIZE,
            POOL_SIZE,
            KEEP_ALIVE_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(QUEUE_SIZE),
            Executors.defaultThreadFactory()
    );
}

c、混合型任务:既有计算又有IO操作,例如http请求。

最佳线程数=(线程等待时间 + 线程cpu时间)/ 线程CPU时间  *  CPU核数。

线程等待时间:DB操作,RPC操作,缓存操作

线程cpu时间:平均线程CPU运行时间

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

科特er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值