文章目录

  • 线程池
  • 前言
  • 1.线程池的创建方式
  • 1.1 newCachedThreadPool方式
  • 1.2 newFixedThreadPool
  • 1.3 newScheduledThreadPool
  • 1.4 newSingleExecutor
  • 2. ThreadPoolExecutor的介绍
  • 2.1 构造方法的核心参数介绍
  • 2.2 线程池的拒绝策略
  • 3.线程池的工作原理


线程池

前言

Java中有许多池,字符串常量池、数据库连接池、线程池。对于线程池来说,我们提前将线程创建好放进池子里,当我们要使用时直接从池子中拿,而不是再从系统申请,我们用完之后,就还给池子而不是直接销毁。从池子里拿线程和还线程的过程中,我们都是纯用户态的操作,而不是直接创建或销毁线程涉及到操作系统的内核态。==这样在频繁创建销毁的情况下,就能节省开销并且提高效率。==以下我们将要介绍Java标准库中现有线程池的使用。

1.线程池的创建方式

Executors是一个工厂类,我们通过它的方法可以创建4种不同风格的线程池,这些方法都是最终通过配置ThreadPoolExecutor类的不同参数实现。Executors本质是对ThreadPoolExecutor类的封装。
ExecutorService表示线程池的一个实例,我们常常用它来接收创建的线程池对象,它的submit方法能够向线程池提交若干个任务。

1.1 newCachedThreadPool方式

该方法是创建一个线程数目动态增长的线程池。如果线程池的长度超过处理需要,就可以灵活回收空闲的线程,如果没有空闲的线程回收,就创建一个新的线程。

举例使用:

package threading;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @author zq
 * 四种创建线程池的方式
 */
public class TD19 {
    public static void main(String[] args) {
        //创建可缓存(动态增长)的线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        //循环加入任务
        int i = 0;
        while (i<5){
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello");
                }
            });
            i++;
        }

    }
}

执行结果:

java 线程池如何存储线程 java中的线程池_java 线程池如何存储线程

1.2 newFixedThreadPool

这种方法是创建一个固定数目的线程池,当任务超过当前线程池的数量时就会一直等待,直到执行。

**举例使用:**创建10大小的线程池,加入五个任务

public static void main(String[] args) {
        //创建一个固定数目的线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        //创建任务
        for (int i = 0; i < 5; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello F");
                }
            });
        }
    }

执行结果

java 线程池如何存储线程 java中的线程池_线程池_02

1.3 newScheduledThreadPool

该方法是可以创建一个定长,支持定时的任务的线程池

举例使用: 我们此时需要用ScheduledExecutorService类型接收我们创建的需要实现定时执行的线程池。

public static void main(String[] args) {
        //创建一个固定数量,支持定时任务,周期任务执行的线程池
        //延迟两秒后执行线程池所有任务
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        for (int i = 0; i < 5; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello S");
                }
            };
            pool.schedule(runnable,2, TimeUnit.SECONDS);
        }

    }

1.4 newSingleExecutor

该方法创建的是一个单线程的线程池,保证所有任务按顺序执行。

举例使用

public static void main(String[] args) {
        //创建单线程线程池
        ExecutorService pool = Executors.newSingleThreadExecutor();
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello1" );
                }
            });
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello2");
                }
            });

        }

执行结果

java 线程池如何存储线程 java中的线程池_线程池_03

2. ThreadPoolExecutor的介绍

我们在实际开发中通常通过new ThreadPoolExecutor的对象创建线程池,这种方法可以更灵活的创建线程池。

2.1 构造方法的核心参数介绍

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

  1. corePoolSize:线程池中的核心线程数,即使空闲也不会死亡。
  2. maximumPoolSize:最大线程数,线程池可容纳的最大数量。
  3. keepAliveTime:闲置超时时间,当活跃线程数大于核心线程数时,超过该时间将会回收非核心的线程。
  4. unit:存活时间的单位。
  5. workQueue:存放任务的阻塞队列,我们可以手动指定一个队列。
  6. handler:超过线程范围和队列容量的线程的任务处理程序==(拒绝策略)==。
  7. threadFactory:工厂模式,创建线程多的辅助类。

2.2 线程池的拒绝策略

在核心参数中我们可以指定一种线程的拒绝策略。在多线程中我们主要有四种拒绝策略:

如图所示

java 线程池如何存储线程 java中的线程池_jvm_04

  1. 第一种AbortPolicy(),满了直接抛异常。
  2. 第二种CallerRunsPolicy(),添加的线程自行负责执行这个任务。(在主线程中运行,不进入线程池)
  3. 第三种DiscardOldestPolicy(),丢弃最老的任务的未处理任务,然后加入新增的任务在等待队列。
  4. 第四种DiscardPolicy(),丢弃最新的任务,就是丢弃这个新增的任务,不抛异常。

3.线程池的工作原理

我们用流程图来描述线程池的工作原理,如图所示:

java 线程池如何存储线程 java中的线程池_线程池_05