线程池

程序的运行,本质上是占用系统的资源,线程的创建、销毁十分销毁资源。优化系统资源的使用可以使用池化技术(线程池、连接池、内存池、对象池… )
池化技术:事先准备好一些资源,有人要用,就从池中拿,用完还回来。

线程池:三大方法、七大参数、四种拒绝策略

线程池的好处:

  • 降低资源的消耗
  • 提高响应的速度
  • 方便管理

线程复用、可以控制最大并发数、管理线程。

三大方法

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// Executors工具类下的3大方法
public class Demo1 {
    public static void main(String[] args) {
        // 单个线程
        //ExecutorService executorService = Executors.newSingleThreadExecutor();
        // 创建一个固定的线程池大小
        //ExecutorService executorService1 = Executors.newFixedThreadPool(3);
        // 可伸缩的,核心线程数为0,最大线程数为 Integer.MAX_VALUE(2的31次方减一 约为21亿)
        ExecutorService executorService2 = Executors.newCachedThreadPool();

        try {
            for (int i = 0; i < 100; i++) {
                executorService2.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executorService2.shutdown();
        }
    }
}

阿里巴巴开发手册不推荐使用Executors创建线程池:

七大参数

源码

public ThreadPoolExecutor(int corePoolSize,          // 核心线程池大小
                              int maximumPoolSize,   // 线程池最大线程数量 
                              long keepAliveTime,    // 空闲线程存活时间
                              TimeUnit unit,		 // 超时单位
                              BlockingQueue<Runnable> workQueue, // 阻塞队列
                              ThreadFactory threadFactory,  // 线程工厂
                              RejectedExecutionHandler handler) {  // 拒绝策略
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

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

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

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  1. corePoolSize 核心线程池大小:线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,它们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。
  2. maximumPoolSize 线程池最大线程数量 :一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接执行,如果没有则会缓存到阻塞队列中,如果阻塞队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
  3. keepAliveTime 空闲线程存活时间:一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定。
  4. unit :keepAliveTime的计量单位
  5. workQueue 阻塞队列 :新任务被提交后,会先进入到此阻塞队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
    ①ArrayBlockingQueue
    基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
    ②LinkedBlockingQuene
    基于链表的阻塞队列(可以设置队列大小,没有设置时队列大小为Interger.MAX),按照FIFO排序。如果不设置队列大小,那么由于队列近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
    ③SynchronousQuene
    一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
    ④PriorityBlockingQueue
    具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
  6. threadFactory 线程工厂 :创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
  7. handler 拒绝策略 :当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,就会采用拒绝策略,jdk中提供了4种拒绝策略。

七大参数可根据下图对比理解,加入你去银行(线程池)办理业务,银行共有五个窗口(线程池最大线程数量),其中只有三个窗口(核心线程池大小)在营业,候客区有三个位置(阻塞队列),如果服务窗口和候客区都满了,此时有人进来,没有营业的窗口开始营业(创建线程)。假如服务窗口的五个位置和候客区的三个位置都满了,就会拒绝进入(拒绝策略)。如果人办理完业务(用完线程放回线程池),走了一部分,此时,新开的两个窗口处于空闲状态,那么经过keepAliveTime时间后,窗口关闭(线程被销毁)。

阻塞队列


四组API

方式抛出异常不抛出异常阻塞等待超时等待
添加add()offer()put()offer(…)
移除remove()poll()take()poll(…)
检测队首元素elementpeek--
public static void test1() {
   // 参数为阻塞队列的大小
    ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

    System.out.println(blockingQueue.add("a")); // true
    System.out.println(blockingQueue.add("b")); // true
    System.out.println(blockingQueue.add("c")); // true
    // 抛出异常:java.lang.IllegalStateException: Queue full
    //System.out.println(blockingQueue.add("d"));

    System.out.println(blockingQueue.remove()); // a
    System.out.println(blockingQueue.remove()); // b
    System.out.println(blockingQueue.remove()); // c
    // 抛出异常:java.util.NoSuchElementException
    // System.out.println(blockingQueue.remove());
}

public static void test2() {
    ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

    System.out.println(blockingQueue.offer("a")); // true
    System.out.println(blockingQueue.offer("b")); // true
    System.out.println(blockingQueue.offer("c")); // true
    System.out.println(blockingQueue.offer("d")); // false 有返回值,不抛出异常

    System.out.println(blockingQueue.poll()); // a
    System.out.println(blockingQueue.poll()); // b
    System.out.println(blockingQueue.poll()); // c
    System.out.println(blockingQueue.poll()); // null  不抛出异常
}

public static void test3() throws InterruptedException {
    ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

    // 没有返回值
    blockingQueue.put("a");
    blockingQueue.put("b");
    blockingQueue.put("c");
    // 队列没有位置了,一直阻塞
    // blockingQueue.put("d");

    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    // 队列为空,一直阻塞
    // System.out.println(blockingQueue.take());
}

public static void test4() throws InterruptedException {
    ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

    System.out.println(blockingQueue.offer("a",2, TimeUnit.SECONDS)); // true
    System.out.println(blockingQueue.offer("b",2, TimeUnit.SECONDS)); // true
    System.out.println(blockingQueue.offer("c",2, TimeUnit.SECONDS)); // true
    System.out.println(blockingQueue.offer("c",2, TimeUnit.SECONDS)); // 等待2秒 返回false

    System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS)); // a
    System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS)); // b
    System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS)); // c
    System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS)); // 等待2秒 返回null

    // 抛出异常:java.util.NoSuchElementException
    // System.out.println(blockingQueue.element());

    System.out.println(blockingQueue.peek()); // 不跑出异常 返回null
}

synchronousQueue同步队列

每个插入操作必须等待另一个线程相应的删除操作,反之亦然。 同步队列没有任何内部容量,甚至没有一个容量。 你不能peek在同步队列,因为一个元素,当您尝试删除它才存在; 您无法插入元素(使用任何方法),除非另有线程正在尝试删除它; 你不能迭代,因为没有什么可以迭代。 队列的头部是第一个排队的插入线程尝试添加到队列中的元素; 如果没有这样排队的线程,那么没有元素可用于删除,并且poll()将返回null 。

自定义线程池

public class Demo4 {
    public static void main(String[] args) {

        ExecutorService threadPool = new ThreadPoolExecutor(
                3,  // corePoolSize
                5,  // maximumPoolSize
                2,  // keepAliveTime 
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),  // 双端队列
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );

        try {
            // 最大承载量:队列大小加上线程池最大容量:5 + 3
            for (int i = 1; i <= 8; i++) {
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "  OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

线程池的最大大小怎样设置:
1.CPU密集型:几核就是几,可以保证CPU的效率最高
2.IO密集型:判断程序中十分消耗IO的线程,若由15个线程十分消耗IO,那么线程池最大线程数量可以设置为30
查看核的数量:
System.out.println(Runtime.getRuntime().availableProcessors()); // 8

四种拒绝策略


new ThreadPoolExecutor.AbortPolicy() :丢弃任务并且抛出异常
new ThreadPoolExecutor.CallerRunsPolicy() :由调用线程执行这个任务
new ThreadPoolExecutor.DiscardPolicy() :丢弃任务不抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy() :把阻塞队列头任务抛弃,把当前任务加到队列中

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值