Executors创建ExecutorService(线程池)的常用方法

一、线程池的创建

JDK中提供了4个可重载的构造方法来创建一个线程池,参数少的构造方法将使用默认的参数调用参数多的构造方法。

构造方法:

public  ThreadPoolExecutor( int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                       TimeUnit unit,
                       BlockingQueue<Runnable> workQueue) {
      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors. defaultThreadFactory(),  defaultHandler);
  }

使用ThreadFactoryBuilder来创建线程池

这里使用guava的ThreadFactoryBuilder来创建线程池

public class ThreadPool {
 
    private static ThreadFactory nameFactory = new ThreadFactoryBuilder()
        .setNameFormat("myExcutor-%d").build();
 
    private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedFactory, new ThreadPoolExecutor.AbortPolicy());
}

线程池的使用:

public class DemoClass {
	public void testMethod() {
		ThreadPool.pool.execute(() -> // 业务处理)
	}
}

参数说明:

  1. corePoolSize(核心线程数):线程池中保持活动状态的最小线程数,即使是空闲状态也不会被回收。

  2. maximumPoolSize(最大线程数):线程池中允许存在的最大线程数,包括核心线程数和非核心线程数。

  3. keepAliveTime(线程空闲时间):当线程池中的线程数超过核心线程数时,空闲线程等待新任务的最长时间,超过这个时间将被回收销毁。

  4. TimeUnit(时间单位):设置keepAliveTime的时间单位,可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。

  5. workQueue(任务队列):用于存放等待执行的任务的阻塞队列,可以选择不同类型的队列,如:
    ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
    LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
    SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
    PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

  6. threadFactory(线程工厂):用于创建线程的工厂类,可以自定义线程的名称、优先级等属性。

  7. rejectedExecutionHandler(任务拒绝策略):当线程池无法接收新任务时,执行的策略,如抛出异常、丢弃任务或者调用主线程来执行任务。


核心线程、最大线程数、线程空闲时间之间的关系:

当有新任务提交到线程池时,首先线程池会尝试使用核心线程来处理任务。
当核心线程都在执行任务且工作队列已满时,线程池会创建新的非核心线程来处理任务,直到达到最大线程数。
当非核心线程完成任务后,如果在线程空闲时间内没有新任务提交,这些线程将被销毁,以避免线程过多占用系统资源。



二、Executors提供了创建ThreadPoolExec
utor的常用方法,主要有:

参考博客:https://blog.csdn.net/u014644594/article/details/103294628

1、 Executors.newFixedThreadPool(int nThreads);

说明:创建固定大小(nThreads, 大小不能超过int的最大值)的线程池

// 线程数量  int nThreads = 20;
// 创建executor 服务
 ExecutorService executor = Executors.newFixedThreadPool(nThreads) ;

重载后的版本,需要传入实现了ThreadFactory接口的对象。

   ExecutorService executor = Executors. newFixedThreadPool(nThreads, threadFactory);

示例:
这里使用guava的ThreadFactoryBuilder来创建线程池

public class ThreadPool {
 
    private static ThreadFactory nameFactory = new ThreadFactoryBuilder()
        .setNameFormat("myExcutor-%d").build();
 
    private static ExecutorService executorService = Executors.newFixedThreadPool(5,nameFactory);

}

线程池的使用

public class DemoClass {
	public void testMethod() {
		ThreadPool.executorService.execute(() -> // 业务处理)
	}
}

说明:创建固定大小(nThreads, 大小不能超过int的最大值) 的线程池,缓冲任务的队列为LinkedBlockingQueue,大小为整型的最大数,当使用此线程池时,在同执行的任务数量超过传入的线程池大小值后,将会放入LinkedBlockingQueue,在LinkedBlockingQueue中的任务需要等待线程空闲后再执行,如果放入LinkedBlockingQueue中的任务超过整型的最大数 时,抛出RejectedExecutionException。

2、Executors.newSingleThreadExecutor():创建大小为1的固定线程池。

ExecutorService executor = Executors.newSingleThreadExecutor();

重载后的版本,需要多传入实现了ThreadFactory接口的对象。

ExecutorService executor = Executors. newSingleThreadScheduledExecutor(ThreadFactory threadFactory) 

说明:创建大小为1的固定线程池,执行任务的线程只有一个,其它的(任务)task都放在LinkedBlockingQueue中排队等待执行。

3、Executors.newCachedThreadPool();创建corePoolSize为0,最大线程数为整型的最大数,线程 keepAliveTime为1分钟,缓存任务的队列为SynchronousQueue的线程池。

ExecutorService executor = Executors.newCachedThreadPool()

当然也可以以下面的方式创建,重载后的版本,需要多传入实现了ThreadFactory接口的对象。

ExecutorService executor = Executors.newCachedThreadPool(ThreadFactory threadFactory) ;

说明:使用时,放入线程池的task任务会复用线程或启动新线程来执行,注意事项:启动的线程数如果超过整型最大值后会抛出RejectedExecutionException异常,启动后的线程存活时间为一分钟。

4、Executors.newScheduledThreadPool(int corePoolSize):创建corePoolSize大小的线程池。

// 线程数量 int corePoolSize= 20;

// 创建executor 服务 
 ExecutorService executor = Executors.newScheduledThreadPool(corePoolSize) ;

重载后的版本,需要多传入实现了ThreadFactory接口的对象。

ExecutorService executor = Executors.newScheduledThreadPool(corePoolSize, threadFactory)

说明:线程keepAliveTime为0,缓存任务的队列为DelayedWorkQueue,注意不要超过整型的最大值。

备注:

FixedThreadPool 模式会使用一个优先固定数目的线程来处理若干数目的任务。规定数目的线程处理所有任务,一旦有线程处理完了任务就会被用来处理新的任务(如果有的话)。这种模式与上面的 CachedThreadPool是不同的。
CachedThreadPool 模式下处理一定数量的任务的线程数目是不确定的。而FixedThreadPool模式下最多的线程数目是一定的。
SingleThreadExecutor 模式只会创建一个线程。它和FixedThreadPool比较类似,不过线程数是一个。 如果多个任务被提交给SingleThreadExecutor的话 ,那么这些任务会被保存在一个队列中,并且会按照任务提交的顺序,一个先执行完成再执行另外一个线程。 SingleThreadExecutor模式可以保证只有一个任务会被执行。 这种特点可以被用来处理共享资源的问题而不需要考虑同步的问题。


即在Java中,常见的线程池类型包括以下几种::

  • FixedThreadPool(固定大小线程池):该线程池维护一个固定数量的线程。无论任务多少,都只会创建固定数量的线程来执行任务。当所有线程都处于忙碌状态时,新任务将被放入队列等待。
  • CachedThreadPool(缓存线程池):该线程池根据任务的数量动态调整线程的数量。如果有可用的空闲线程,则会重用它们;如果没有可用的线程,则会创建新的线程。如果线程空闲时间过长,超过一定阈值,那么这些空闲线程将会被终止并移除。
  • SingleThreadPool(单线程池):该线程池只包含一个线程,用于顺序执行所有任务。即使任务异常或终止,也会有新线程取代它。适用于需要按顺序执行任务的场景。
  • ScheduledThreadPool(调度线程池):该线程池用于执行延迟任务或周期性任务。可以指定任务的执行时间或间隔,并由线程池自动触发执行。
  • WorkStealingPool(工作窃取线程池):该线程池是Java
    8新增的一种线程池类型,基于Fork/Join框架实现。它使用一种工作窃取算法,可以高效地处理大量的任务。它会尝试从其他线程的队列中“窃取”任务来执行。

线程的状态:
在线程的生命周期中,线程可以处于不同的状态。Java中的线程共有6种状态:

  1. 新建(New):通过创建Thread对象来新建一个线程,但还未调用start方法。
  2. 可运行(Runnable):线程正在Java虚拟机中执行,或者等待系统资源的分配,在就绪队列中等待被调度执行。
  3. 运行(Running):线程正在执行其任务代码。
  4. 阻塞(Blocked):线程被阻塞并等待监视器锁,通常发生在获取同步锁失败、执行sleep方法或等待输入输出等操作时。
  5. 等待(Waiting):线程正在等待其他线程的特定操作,通过调用wait或join方法等进入该状态。
  6. 终止(Terminated):线程已经执行完其任务代码,终止运行。

yield、sleep、wait和join是Java中用于线程控制的方法,它们之间有以下区别:

  1. yield()方法:

    • yield()方法是Thread类的静态方法,可以通过Thread.yield()调用。
    • yield()方法会让当前线程主动放弃CPU执行时间,使得其他具有相同优先级的线程有机会执行。
    • yield()方法不会进入阻塞状态,而是重新进入就绪状态,等待CPU调度器分配执行时间。
  2. sleep()方法:

    • sleep()方法是Thread类的静态方法,可以通过Thread.sleep()调用。
    • sleep()方法会使当前线程暂停执行,进入阻塞状态,但不会释放锁资源。
    • sleep()方法参数为时间,指定线程暂停的时间段,单位是毫秒(也可以使用纳秒)。
    • 在睡眠时间结束后,线程会重新进入就绪状态,等待CPU执行。
  3. wait()方法:

    • wait()方法是Object类的方法,可以通过对象实例调用。
    • wait()方法会使当前线程暂停执行,进入阻塞状态,同时释放对象的同步锁资源。
    • wait()方法必须在synchronized代码块或方法中调用。
    • 调用wait()方法后,必须通过其他线程调用该对象的notify()或notifyAll()方法来唤醒等待的线程。
  4. join()方法:

    • join()方法是Thread类的方法,可以通过Thread.join()调用。
    • join()方法会让当前线程等待被调用线程执行完毕,即当前线程暂停执行,直到被调用线程执行完毕或超时。
    • join()方法可以用于线程间的协作,使得多个线程按预期的顺序执行。
    • 如果调用join()方法的线程已经是终止状态(Terminated),那么join()方法会立即返回。

总结:
yield()方法是让出CPU执行时间给同优先级的其他线程;
sleep()方法是使当前线程暂停执行一段时间;
wait()方法是使当前线程进入阻塞状态并释放锁资源,需要通过其他线程来唤醒;
join()方法是让当前线程等待被调用线程执行完毕。


线程池的使用,可参考博客:https://blog.csdn.net/m0_53714343/article/details/131438982

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值