线程池的使用——创建线程

  • 线程池的创建
  • 线程池的创建方式
  • Executors.newFixedThreadPool:
  • Executors.newCachedThreadPool:
  • Executors.newSingleThreadExecutor:
  • Executors.newScheduledThreadPool:
  • Executors.newSingleThreadScheduledExecutor:
  • Executors.newWorkStealingPool:
  • ThreadPoolExecutor:
  • 线程池的拒绝策略
  • ThreadPoolExecutor.AbortPolicy:
  • ThreadPoolExecutor.DiscardPolicy:
  • ThreadPoolExecutor.DiscardOldestPolicy:
  • ThreadPoolExecutor.CallerRunsPolicy:


线程池的创建

线程池的创建方法总共有 7 种,但总体来说可分为 2 类:

  • 一类是通过 ThreadPoolExecutor 创建的线程池;
  • 另一个类是通过 Executors 创建的线程池。

线程池的创建方式

Executors.newFixedThreadPool:

创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待

//      创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        // 创建任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            }
        };
            executorService.submit(runnable);  // 执行方式 1:submit
            executorService.execute(runnable); // 执行方式 2:execute
            executorService.execute(runnable);
            executorService.execute(runnable);
            //结束线程池
            executorService.shutdown();

执行结果如下:
超出线程数量的任务会在队列中等待,其他任务执行完毕之后再获取线程执行任务。

任务被执行,线程:pool-1-thread-1
任务被执行,线程:pool-1-thread-3
任务被执行,线程:pool-1-thread-2
任务被执行,线程:pool-1-thread-3

Executors.newCachedThreadPool:

创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;

ExecutorService executorService = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
        // 创建任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            }
        };
        // 线程池执行任务(一次添加 4 个任务)
        // 执行任务的方法有两种:submit 和 execute
        for (int i = 0; i < 10 ; i++) {
            executorService.submit(runnable);  // 执行方式 1:submit
            executorService.execute(runnable); // 执行方式 2:execute
            executorService.execute(runnable);
            executorService.execute(runnable);
        }
        executorService.shutdown();

运行结果如下:

任务被执行,线程:pool-1-thread-1
任务被执行,线程:pool-1-thread-1
任务被执行,线程:pool-1-thread-3
任务被执行,线程:pool-1-thread-4
任务被执行,线程:pool-1-thread-2
任务被执行,线程:pool-1-thread-5
任务被执行,线程:pool-1-thread-6
任务被执行,线程:pool-1-thread-7
任务被执行,线程:pool-1-thread-9
任务被执行,线程:pool-1-thread-10
任务被执行,线程:pool-1-thread-14
任务被执行,线程:pool-1-thread-13
任务被执行,线程:pool-1-thread-15
任务被执行,线程:pool-1-thread-16
任务被执行,线程:pool-1-thread-18
任务被执行,线程:pool-1-thread-20
任务被执行,线程:pool-1-thread-17
任务被执行,线程:pool-1-thread-22
任务被执行,线程:pool-1-thread-23
任务被执行,线程:pool-1-thread-25
任务被执行,线程:pool-1-thread-27
任务被执行,线程:pool-1-thread-28
任务被执行,线程:pool-1-thread-30
任务被执行,线程:pool-1-thread-29
任务被执行,线程:pool-1-thread-32
任务被执行,线程:pool-1-thread-31
任务被执行,线程:pool-1-thread-33
任务被执行,线程:pool-1-thread-35
任务被执行,线程:pool-1-thread-39
任务被执行,线程:pool-1-thread-36
任务被执行,线程:pool-1-thread-8
任务被执行,线程:pool-1-thread-19
任务被执行,线程:pool-1-thread-21
任务被执行,线程:pool-1-thread-24
任务被执行,线程:pool-1-thread-34
任务被执行,线程:pool-1-thread-38
任务被执行,线程:pool-1-thread-11
任务被执行,线程:pool-1-thread-12
任务被执行,线程:pool-1-thread-26
任务被执行,线程:pool-1-thread-37

Executors.newSingleThreadExecutor:

创建单个线程数的线程池,它可以保证先进先出的执行顺序;

ExecutorService executorService = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
        // 创建任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            }
        };
        // 线程池执行任务(一次添加 4 个任务)
        // 执行任务的方法有两种:submit 和 execute
        for (int i = 0; i < 10 ; i++) {
            executorService.submit(runnable);  // 执行方式 1:submit
            executorService.execute(runnable); // 执行方式 2:execute
            executorService.execute(runnable);
            executorService.execute(runnable);
        }
        executorService.shutdown();

执行结果如下

任务被执行,线程:pool-1-thread-1
任务被执行,线程:pool-1-thread-1
任务被执行,线程:pool-1-thread-1
任务被执行,线程:pool-1-thread-1
任务被执行,线程:pool-1-thread-1
任务被执行,线程:pool-1-thread-1
...

Executors.newScheduledThreadPool:

创建一个可以执行延迟任务的线程池;

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
       System.out.println("任务添加时间"+ new Date());
        ScheduledFuture<Date> schedul = scheduledExecutorService.schedule(()->{
            return new Date();
        }, 3, TimeUnit.SECONDS);
        System.out.println("任务执行完毕" + schedul.get());

运行结果如下:

在2秒钟后执行一次任务,线程阻塞等待结果返回。执行完成并返回结果后就结束。

任务添加时间Thu Mar 02 20:50:12 CST 2023
任务执行完毕Thu Mar 02 20:50:15 CST 2023

Executors.newSingleThreadScheduledExecutor:

创建一个单线程的可以执行延迟任务的线程池;

public static void SingleThreadScheduledExecutor() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
    // 添加定时执行任务(2s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 2, TimeUnit.SECONDS);
}

执行结果如下:

grpc创建线程池 线程池创建线程_java

Executors.newWorkStealingPool:

创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。

public static void workStealingPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newWorkStealingPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
        });
    }
    // 确保任务执行完成
    while (!threadPool.isTerminated()) {
    }
}

运行结果如下:

从上述结果可以看出,任务的执行顺序是不确定的,因为它是抢占式执行的。

grpc创建线程池 线程池创建线程_grpc创建线程池_02

ThreadPoolExecutor:

最原始的创建线程池的方式,它包含了 7 个参数可供设置。

public static void myThreadPoolExecutor() {
    // 创建线程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

执行结果如下:

grpc创建线程池 线程池创建线程_java_03

线程池的拒绝策略

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

ThreadPoolExecutor.AbortPolicy:

丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy:

丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:

丢弃队列最前面的任务,然后重新提交被拒绝的任务

ThreadPoolExecutor.CallerRunsPolicy:

由调用线程(提交任务的线程)处理该任务