多线程之线程池详解

线程池优势

线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。它的主要特点为:线程复用;控制最大并发数;管理线程。

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
第二:提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

实现

在Java中提供了两种类型的线程池来供开发人员使用,分别是ThreadPoolExecutor和ScheduledThreadPoolExecutor。其中ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,类的UML图如下所示。ScheduledThreadPoolExecutor的功能和Java中的Timer类似,它提供了定时的去执行任务或者固定时延的去执行任务的功能,其功能比Timer更加强大。
在这里插入图片描述

代码演示

void execute(Runnable command)
/*
 * 线程池:提供了一个线程队列,队列中保存着所有等待状态的线程,避免了创建于销毁线程的额外开销
 * 提高了响应的速度
 * 二.线程池的体系结构
 * juc包中的.Executor:负责线程的使用和调度的根接口
 * ----**ExecutorService**子接口:线程池的主要接口
 * -----------ThreadPoolExecutor线程池的实现类
 * -----------ScheduleExecutorService子接口,用于线程的调度
 * ----------------ScheduleThreadPoolExcutor:继承了ThreadPoolExecutor,实现了ScheduleExecutorService接口
 *
 * 三.工具类:Executors
 * newFixedThreadPool:创建固定大小的线程池
 * newCaschedThreadPool:缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
 */
public class Demo01_ThreadPool {
    public static void main(String args[]) throws InterruptedException {
        //ExecutorService pool = Executors.newFixedThreadPool(5); //一个线程池中五个受理线程
        //ExecutorService pool = Executors.newSingleThreadExecutor(); //一个线程池中一个受理线程
        ExecutorService pool = Executors.newCachedThreadPool(); //一池n线程
        // 模拟有十个顾客来银行办理业务,目前有五个员工(线程)
        try {
            for (int i = 0; i < 10; i++) {
                TimeUnit.MILLISECONDS.sleep(300);
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "-->办理业务");
                });
            }
        } finally {
            pool.shutdown();
        }
    }
}

在这里插入图片描述

Future submit(Callable task)
public class Demo06_ThreadPool_submit {
    public static void main(String args[]) throws InterruptedException, ExecutionException {
        ExecutorService pool = Executors.newCachedThreadPool(); //一池n线程
        // 模拟有十个顾客来银行办理业务,目前有五个员工(线程)
        List<Future> futures = new ArrayList<>();
        try {
            for (int i = 0; i < 10; i++) {
                TimeUnit.MILLISECONDS.sleep(300);
                Future<String> future = pool.submit(() -> {
                    System.out.println(Thread.currentThread().getName() + "-->返回随机数");
                    return new Random().nextInt() + "";
                });
                futures.add(future);
            }
        } finally {
            pool.shutdown();
        }

        for (Future future : futures) {
            System.out.println(future.get());
        }
    }
}

在这里插入图片描述

线程池该用哪个?

答:Executors提供的一个都不用,使用时自定义线程池

来源:《阿里巴巴Java开发手册》
【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,
这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM

自定义线程池代码示例:

public class Demo02_自定义线程池 {
    public static void main(String args[]) throws InterruptedException {
        System.out.println(Runtime.getRuntime().availableProcessors()); //cpu核数
        ExecutorService pool = new ThreadPoolExecutor(
                2,		// corePoolSize
                5,      // maximumPoolSize=cpu核数+1or2
                2L,		// keepAliveTime
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),	// capacity
                Executors.defaultThreadFactory(), // threadFactory
                new ThreadPoolExecutor.DiscardOldestPolicy()); // 任务拒绝策略
			
        try {
            // 整个最大容纳数等于maximumPoolSize+队列容量=5+3=8
            for (int i = 0; i < 10; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "-->办理业务");
                });
            }
        } finally {
            pool.shutdown();
        }
    }
}

七大参数

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

参数解释

参数 功能
int corePoolSize 线程池的核心线程数
int maximumPoolSize 线程池的最大线程数
long keepAliveTime 空闲线程的最大空闲时间
TimeUnit unit 空闲时间的单位,TimeUnit是一个枚举值,它可以是纳秒、微妙、毫秒、秒、分、小时、天
BlockingQueue workQueue 存放任务的阻塞队列,常用的阻塞队列有ArrayBolckingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityQueue
ThreadFactory threadFactory 创建线程的工厂,通常利用线程工厂创建线程时,赋予线程具有业务含义的名称
RejectedExecutionHandler handler 拒绝策略。当线程池线程数超过最大线程数时,线程池无法再接收任务了,这个时候需要执行拒绝策略

线程池工作流程

在这里插入图片描述

阻塞队列

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue: 一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue: 一个不存储元素的阻塞队列。
LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。

拒绝策略

ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。 (默认)
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务。(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

GitHub:多线程相关Demo

发布了67 篇原创文章 · 获赞 21 · 访问量 2693

猜你喜欢

转载自blog.csdn.net/qq_44779506/article/details/104965100