Java线程池

生命周期

RUNNING 运行状态,能够接受新任务且能对已添加的任务进行处理。线程池一旦被创建就处于RUNNING状态。

SHUTDOWN 关闭状态,不接受新任务,但可以处理已添加的任务。RUNNING状态的线程池调用shutdown()后会进入SHUTDOWN状态。

STOP 停止状态,不接收新任务,清空已添加的任务,且会中断正在执行任务的线程。RUNNING状态的线程池调用了shutdownNow()后会进入STOP状态。

TIDYING 当所有任务已终止,且任务数量为0时,线程池会进入TIDYING。当线程池处于SHUTDOWN状态时,阻塞队列中的任务被执行完了,且线程池中没有正在执行的任务了,状态会由SHUTDOWN变为TIDYING。当线程处于STOP状态时,线程池中没有正在执行的任务时则会由STOP变为TIDYING。

TERMINATED 终止状态。处于TIDYING状态的线程执行terminated()后进入TERMINATED状态。

日常使用

ThreadPoolExecutor executor = new ThreadPoolExecutor();

executor.execute();

executor.shutdown();//关闭线程池,不接受新任务,但会把已添加的任务执行完

executor.shutdownNow();//关闭线程池,不接受新任务,终止正在执行的任务,清空并返回当前阻塞队列中的所有任务

使用jdk快速创建线程池

Executors.newSingleThreadExecutor();

Executors.newCachedThreadPool();

Executors.newScheduledThreadPool();

Executors.newFixedThreadPool();

构造方法各参数含义

corePoolSize 核心线程数。当有新任务时,如果线程数小于corePoolSize,那么则直接创建新的线程来执行任务。

workQueue 任务队列,它是一个阻塞队列,存储待执行的任务。当有新任务时,如果线程数>= corePoolSize,就把任务被放到队列中。

maximumPoolSize 线程池支持的最大线程数。当有新任务时,如果线程数>=corePoolSize,并且workQueue已满,则会创建新线程执行任务,但是线程数要小于等于maximumPoolSize。

keepAliveTime 非核心线程空闲时保持存活的时间。非核心线程即workQueue满了之后,再提交任务时创建的线程,因为这些线程不是核心线程,所以它空闲时间超过keepAliveTime后则会被回收。

unit 非核心线程空闲时保持存活的时间的单位

threadFactory 创建线程的工厂,可以在这里统一处理创建线程的属性

handler 拒绝策略。当线程池中的线程达到maximumPoolSize线程数后且workQueue已满,再有新任务时,执行拒绝策略 -> RejectedExecutionHandler接口,jdk默认有4种实现:

DiscardPolicy 丢弃新任务

DiscardOldestPolicy 丢弃等待队列中最早的任务,并把新任务重新execute

CallerRunsPolicy 会在提交新任务的那个线程去执行新任务

AbortPolicy 直接抛出RejectedExecutionException异常

⚠️注意

1、当线程数超过corePoolSize后,那么空闲时间超过keepAliveTime的线程会被销毁,但不管销毁多少个,池子中至少会存在corePoolSize个线程

2、如果线程池存在空闲的线程,并且设置了allowCoreThreadTimeOut为true,那么空闲时间超过keepAliveTime的核心线程也会被销毁。

3、corePoolSize只代表线程数的一个阈值,线程本身没有核心、非核心之说,当线程数大于corePoolSize时,任何线程只要空闲超时,就会被销毁

4、线程池中的AtomicInteger ctl字段,前3位表示线程池状态,后29位表示当前线程数

⚠️execute方法源码解析

addWorker //线程池内部有一个 HashSet<Worker> workers,每个Worker内部持有一个线程,此方法会新建Worker对象并添加到workers集合中,然后start线程,在此线程中执行runWorker方法

runWorker //首先,为保证线程安全,Worker继承自AQS,执行任务时流程为:lock,执行task,unlock。 其次,runWorker 内部是通过while循环调用线程池的getTask方法,来不断获取task(也就是Runnable对象),不为空则执行task,然后继续获取;一旦为空,则跳出循环,执行processWorkerExit方法,将worker移除,表示线程执行结束,所以我们要重点关注getTask返回null的情况

getTask //内部是一个无限for循环,不断尝试从workQueue中取任务,workQueue是一个BlockingQueue,具体实现以LinkedBlockingQueue为例,内部使用了ReentrantLock来保证线程安全。取任务时,根据 allowCoreThreadTimeOut || workerSize > corePoolSize 的值分两种情况,若为true,说明允许当前线程空闲超时,调用workQueue.poll(keepAliveTime);若为false,说明当前线程是核心线程,且不允许空闲超时,调用workQueue.take()。这两个方法内部逻辑相似,都是用while循环判断队列是否为空,为空则阻塞当前线程;不为空则退出循环并返回队首元素。不同点在于take方法会一直阻塞线程,直到返回有效数据;poll(keepAliveTime)方法如果阻塞时间超过keepAliveTime,则会返回null,从而使getTask返回null,最终导致线程结束

workQueue.poll(keepAliveTime) //内部通过调用ConditionObject.awaitNanos(nanos) => LockSupport.parkNanos(nanos) => sun.misc.Unsafe.park(nanos) 来实现阻塞线程

workQueue.take() //内部通过调用ConditionObject.await() => LockSupport.park() => sun.misc.Unsafe.park(0) 来实现阻塞线程

Unsafe.park() //线程挂起后可以使用unpark来恢复。java.util.concurrent包中挂起操作都是在LockSupport类实现的,也正是使用这两个方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值