Java线程池

1.什么是线程池

线程池就是由很多线程,加上一个任务(实现了Runnable接口的类)队列组成,线程池中的线程,自己可以定义,当向线程池中发任务时,如果有线程空闲,线程就将任务带给CPU处理,如果没有线程空闲,就存储在任务队列中。

2. 为什么要用线程池

1)可以提高效率,当有很多的线程需要执行的时候,需要频繁的创建和销毁线程,很大程度上影响处理效率,利用线程池,可以不用频繁的创建和销毁线程,重用存在的线程,减少线程的创建,提高性能
2)能够有效控制线程的最大并发数,防止因为系统资源不足,而产生阻塞的情况
3)能够简单的管理线程,如进行延时、定时执行等。

3.线程池底层原理

java中,有一个Executor接口,是线程池的顶级接口,这个接口只有一个execute(Runnable r)方法,execute方法在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定,执行execute方法,一般来说,Runnable任务开辟在新线程中的使用方法为:new Thread(new RunnableTask())).start(),但在Executor中,可以使用Executor而不用显示地创建线程:executor.execute(new RunnableTask()),异步执行,也就是说,Executor可以替代Thread的创建,java线程池,就是利用这个Executor接口实现的,Executor就相当于一个出租车,而线程池就相当于一个出租车公司。

public class Thread5 implements Executor {
	 public static void main(String[] args) {
		 new Thread5().execute(new Runnable(){

				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName());
				}
			});
	}
	@Override
	public void execute(Runnable command) {
		new Thread(command).start();
		
	}
}

4.线程池的创建

在java中,可以使用Java.util.concurrent.Executors这个类来进行创建

ExecutorService service = Executors.newFixedThreadPool(5);//固定线程数的线程池

底层源码如下

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

从源码也可以看出,线程池的创建是通过new ThreadPoolExecutor这个类来创建的,通过Executors这个类创建的线程池是jdk提供的,实际使用中,可能并不适用实际情况,所以可以通过ThreadPoolExecutor这个类来创建合适的线程池。下面来看一下ThreadPoolExecutor这个类中的构造方法

//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

//六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

//六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)

//七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

int corePoolSize => 该线程池中核心线程数最大值

核心线程:线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程,核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态),如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉,很好理解吧,正常情况下你不干活我也养你,因为我总有用到你的时候,但有时候特殊情况(比如我自己都养不起了),那你不干活我就要把你干掉了。

int maximumPoolSize

该线程池中线程总数最大值,线程总数 = 核心线程数 + 非核心线程数。核心线程在上面解释过了,这里说下非核心线程:不是核心线程的线程。

long keepAliveTime

该线程池中非核心线程闲置超时时长,一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉, 如果设置allowCoreThreadTimeOut = true,则会作用于核心线程。

TimeUnit unit

keepAliveTime的单位,TimeUnit是一个枚举类型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天

BlockingQueue workQueue

该线程池中的任务队列:维护着等待执行的Runnable对象,当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。
常用的workQueue类型:

  • SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大。

  • LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize

  • ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

  • DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

ThreadFactory threadFactory

创建线程的方式,这是一个接口,你new他的时候需要实现他的Thread newThread(Runnable r)方法,一般用不上,默认即可
小伙伴应该知道AsyncTask是对线程池的封装吧?那就直接放一个AsyncTask新建线程池的threadFactory参数源码吧:

    new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread Thread(Runnable r) {
            return new Thread(r,"AsyncTask #" + mCount.getAndIncrement());
        }
    }

这么简单?就给线程起了个名

RejectedExecutionHandler handler

拒绝策略,当工作队列、线程池全满时如何拒绝新任务,默认抛出异常。

新建一个线程池的时候,一般只用前五个参数

corePoolSize,maximumPoolSize,workQueue之间关系。
当线程池中线程数小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
当线程池中线程数达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行 。
当workQueue已满,且maximumPoolSize > corePoolSize时,新提交任务会创建新线程执行任务。
当workQueue已满,且提交任务数超过maximumPoolSize,任务由RejectedExecutionHandler处理。
当线程池中线程数超过corePoolSize,且超过这部分的空闲时间达到keepAliveTime时,回收这些线程。
当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize范围内的线程空闲时间达到keepAliveTime也将回收。

通过ThreadPoolExecutor.execute(Runnable command)方法即可向线程池内添加一个任务,execute方法的执行流程图如下:
在这里插入图片描述jdk提供的线程池

  • FixedThreadPool,通过Exectuors这个类来创建,线程池中只能创建固定数量的核心线程数,当线程池中线程数量达到核心线程数,就将任务放到队列中,队列满了,就会报错,但是,队列一般不会满,容量为Integer.MAX_VALUE
//Executors类中源码
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

  • CachedThreadPool ,可缓存线程池,线程数无限制,自动扩容。容量管理策略:如果线程 池中的线程数量不满足任务执行,创建新的线程。每次有新任务无法即时处理的时候,都会 创建新的线程。当线程池中的线程空闲时长达到一定的临界值(默认 60 秒),自动释放线程。默认线程空闲 60 秒,自动销毁。
//Executors类中,创建CachedThreadPool方法
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
//

  • ScheduledThreadPool,支持定时及周期性任务执行。使用的是DelayQueue
//Executors类中newScheduledThreadPool(int corePoolSize)方法
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
//ExecutorThreadPool源码
 public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

  • SIngleThreadPool单线程化的线程池:有且仅有一个工作线程执行任务,所有任务按照指定顺序执行,即遵循队列的入队出队规则
    创建方法:
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

//源码:
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

线程池的创建有很多方式,上述方式其实还是通过ThreadPoolExecutor这个类创建的,ThreadPoolExecutor,这个类继承了AbstractExecutorService(抽象类),AbstractExecutorService实现了ExecutorService接口,ExecutorService实现了Executor接口,创建线程池的类,都要实现Executor接口。

ExecutorService,这个接口实现了Executor,可以理解为线程池服务类型,所有的线程池类都实现了这个接口,实现这个接口,就拥有了线程池的能力,这个接口中有管理线程池的方法,常用方如下:

  • shutdown():优雅的关闭线程池,等对列中的任务都执行完之后,才可以关闭,
  • List shutDownNow(): 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
  • isShutdown:是否执行过shutdown方法,执行过就返回true,没有执行过就返回false。
  • isTerminated:线程池是否关闭
  • Future submit(Callable task): 提交一个返回值的任务用于执行,返回一个表示任务的未来结果的 Future。
  • Future<?> submit(Runnable task): 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。

Future 未来结果,代表线程任务执行结束后的结果。获取线程执行结果的方式是通过 get 方法获取的。get 无参,阻塞等待线程执行结束,并得到结果。get 有参,阻塞固定时长,等待 线程执行结束后的结果,如果在阻塞时长范围内,线程未执行结束,抛出异常。 常用方法: T get() T get(long, TimeUnit)

Callable 可执行接口。 类似 Runnable 接口。也是可以启动一个线程的接口。其中定义的方法是 call。call 方法的作用和 Runnable 中的 run 方法完全一致。call 方法有返回值。 接口方法 : Object call();相当于 Runnable 接口中的 run 方法。区别为此方法有返回值。 不能抛出已检查异常。 和 Runnable 接口的选择 - 需要返回值或需要抛出异常时,使用 Callable,其他情况可 任意选择。

参考:
线程池,这一篇或许就够了
通俗易懂,各常用线程池的执行 流程图

如有不足之处,欢迎指正,谢谢!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值