java 线程池

线程的创建方式

  1. 继承 Thread
  2. 实现runnable
  3. 实现Callable ( 前三种实现
  4. 线程池(java提供了Executors可以去创建,推荐手动创建线程池)

Executors创建线程池

	//创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
		Executors.newCachedThreadPool();
		//创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
		Executors.newFixedThreadPool(5);
		//创建一个定长线程池,支持定时及周期性任务执行
		Executors.newScheduledThreadPool(10);  
		//创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
		//保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
		Executors.newSingleThreadExecutor();

ThreadPoolExecutor线程池

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
 
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}
 
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}
/**
	 * @param corePoolSize 核心线程数
	 * @param maximumPoolSize 最大线程数
	 * @param keepAliveTime  keepAliveTime当线程数大于核心时,这是多余空闲线程在终止前等待新任务的最长时间
	 * @param unit   keepAliveTime参数的时间单位
	 * @param workQueue  在执行任务之前用于容纳任务的队列。此队列将只包含由{@code execute}方法提交的{@codeRunnable}任务
	 * @param threadFactory 执行器创建新线程时要使用的工厂
	 * @param handler 当由于达到线程边界和队列容量而阻止执行时要使用的处理程序
	 */
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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

可以看到,其需要如下几个参数:
1.corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
2.maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
3.keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
4.unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
5.workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
6.threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
7.handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。

线程池的使用流程如下

// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
                                             MAXIMUM_POOL_SIZE,
                                             KEEP_ALIVE,
                                             TimeUnit.SECONDS,
                                             sPoolWorkQueue,
                                             sThreadFactory);
// 向线程池提交任务
threadPool.execute(new Runnable() {
    @Override
    public void run() {
        ... // 线程执行的任务
    }
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表

线程池五种状态

线程池ThreadPoolExecutor分别有五种状态分别为:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED使用一个AtomicInteger类型的ctl字段来描述线程池地运行状态和线程数量,通过ctl的高三位来表示线程池的5种状态,低29位表示线程池中现有的线程数量。

 	private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    /**
     * 线程池线程数地bit数
     */
    private static final int COUNT_BITS = Integer.SIZE - 3;
    /**
     * 线程池中最大线程容量
     */
    private static final int CAPACITY = (1 << COUNT_BITS) - 1;

    /**
     * 表示可接受新任务,且可执行队列中的任务;
     */
    private static final int RUNNING = -1 << COUNT_BITS;
    /**
     * 表示不接受新任务,但可执行队列中的任务;
     */
    private static final int SHUTDOWN = 0 << COUNT_BITS;
    /**
     * 表示不接受新任务,且不再执行队列中的任务,且中断正在执行的任务;
     */
    private static final int STOP = 1 << COUNT_BITS;
    /**
     * 所有任务已经中止,且工作线程数量为0,最后变迁到这个状态的线程将
     * 要执行terminated()钩子方法,只会有一个线程执行这个方法;
     */
    private static final int TIDYING = 2 << COUNT_BITS;
    /**
     * TERMINATED,中止状态,已经执行完terminated()钩子方法;
     */
    private static final int TERMINATED = 3 << COUNT_BITS;

线程池处理流程

    /**
     * 在将来某个时候执行给定的任务。该任务可以在新线程中执行,也可以在现有的池线程中执行。如果由于此执行
     * 器已关闭或已达到其容量,任务无法提交执行,则由当前RejectedExecutionHandler处理该任务。
     * Params:
     * 命令–要执行的任务
     * Throws:
     * RejectedExecutionException–如果任务无法接受执行,则由RejectedExecutionHandler自行决定
     * NullPointerException–如果命令为null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         分三步走:

         1.如果运行的线程少于corePoolSize,请尝试以给定的命令作为第一个任务来启动一个新线程。
         对addWorker的调用原子地检查runState和workerCount,因此,通过返回false,可以防止
         在不应该添加线程的情况下添加线程的错误警报。

         2.如果一个任务可以成功排队,那么我们仍然需要仔细检查我们是否应该添加一个线程(因为自上
         次检查以来已有的线程已经失效),或者池是否在进入该方法后关闭。因此,如果有必要,我们重
         新检查状态,如果停止,则回滚排队,如果workerCount为0,则启动一个新线程。

         3.如果我们不能对任务进行排队,那么我们尝试添加一个新线程。如果它失败了,我们知道线程池
         被关闭或饱和了,所以拒绝了这项任务。
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (!isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        } else if (!addWorker(command, false))
            reject(command);
    }

处理流程如下如图所示
在这里插入图片描述

实现示例

public static void main(String[] args) {
		
	ArrayBlockingQueue abq = new ArrayBlockingQueue<>(8);
	ThreadPoolExecutor executor= new ThreadPoolExecutor(5,10,20,TimeUnit.SECONDS,abq);
	//创建线程任务
	/* i<15  5个核心线程,8个队列,2个非核心线程 */
	/* i<20  5个核心线程,8个队列,5个非核心线程 */
	for (int i = 0; i < 15; i++) {
		MyRunnable myRunnable = new MyRunnable();
		executor.execute(myRunnable);
	}	
	executor.shutdown();	
}
public class MyRunnable implements Runnable{
	@Override
	public void run() {

		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"******"+new Date());
		}
	}
}

线程池大小优化

线程池的最佳大小取决于可用的处理器数量和待处理任务的性质。

  1. 对于CPU密集型任务,假设系统有N个逻辑处理核心
    N 或 N+1 的最大线程池数量大小将实现最大效率
  2. 对于 I/O密集型任务,需要考虑请求的等待时间(W)和服务处理时间(S)的比例,
    线程池最大大小为 N*(1+ W/S)会实现最高效率

具体可以根据反复测试调试更优结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值