线程池

线程池

一、为什么需要线程池

优点:

  1. 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁带来的消耗。
  2. 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行。
  3. 提高线程的可管理性:线程池可以统一进行线程分配、调度和监控。
二、创建线程池的两种方式
1、Executors

第一种Executors: 具体的4种常用的线程池实现如下:(返回值都是ExecutorService)

public class Test1 {
    // 创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。
    private ExecutorService pool1 = Executors.newFixedThreadPool(4);

    // 可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。
    // 如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务
    // 总结:可伸缩的,遇强则强,遇弱则弱
    private ExecutorService pool2 = Executors.newCachedThreadPool();

    // 创建一个定长的线程池,支持定时及周期性任务执行
    private ExecutorService pool3 = Executors.newScheduledThreadPool(3);

    // 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
    // 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    private ExecutorService pool4 = Executors.newSingleThreadExecutor();
}

知道了第一种的线程的创建方式,怎么使用呢,接着看下面的代码!

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

public class Test2 {
    public static void main(String[] args) {
        // ExecutorService pool = Executors.newSingleThreadExecutor();
        // ExecutorService pool = Executors.newFixedThreadPool(5);
        // ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);

        ExecutorService pool = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10 ; i++) {
            	// 为什么需要定义这个变量?这个其实就是一个临时变量
            	// 因为在后面new Runnable的代码块中不能直接使用到i,所以创建了这个临时的变量
                final int temp = i;
                // 使用线程池创建线程
                pool.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName()+":"+ temp);
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完需要关闭
            pool.shutdown();
        }
    }
}

知道了如何使用,下面我们就来看看第一种利用Executors创建方式的源码

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,//21亿
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

通过上面源码,我们发现这几种创建线程池本质就是:调用ThreadPoolExecutor()方法

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;
}

知道了创建线程的根本方法就是ThreadPoolExecutor(),下面我们就来详细看看这种创建方式,也就是创建线程池的第二种方法!

2、ThreadPoolExecutor

第二种: 线程池的返回值ThreadPoolExecutor简介:

private ThreadPoolExecutor pool = new ThreadPoolExecutor(
    3,3,0, TimeUnit.MICROSECONDS,
    new LinkedBlockingDeque<>(),new ThreadPoolExecutor.AbortPolicy();// 这里有6个参数
2.1、解释六个参数:

第1个参数: 线程池维护线程的最少数量 (core : 核心)

第2个参数: 线程池维护线程的最大数量

第3个参数: 线程池维护线程所允许的空闲时间

第4个参数: 线程池维护线程所允许的空闲时间的单位 。

​ 如果超过3这个数量,4这个时间单位,2-1(最大线程数-核心线程数)这些线程就会关闭

第5个参数: 线程池所使用的缓冲队列

第6个参数: 线程池对如果超出工作队列的长度,任务要处理的方式,即拒绝任务的处理策略 (有四种)

2.2、四种策略
  • CallerRunsPolicy:谁指派的任务,让他自己去执行(哪里来的去哪里!)

  • AbortPolicy:始终抛出一个异常,让任务处理类处理

  • DiscardPolicy:丢弃最新的任务(队列满了,丢掉任务,不会抛出异常!)

  • DiscardOldestPolicy:丢弃最久的最老的任务(队列满了,尝试和最早的竞争,也不会抛出异常!)

2.3、画图解释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-19K9JkPK-1585404992834)(C:\Users\惠秋丽\AppData\Roaming\Typora\typora-user-images\1585190043061.png)]

解释一下这个图

有一家银行,有五个柜台,一般情况下都是都只开启两个柜台(1,2柜台),其他三个柜台(3,4,5柜台)都不开门,有一天来了两个人来办理业务,很自然的就坐到了前两个柜台。这个时候又来了3个人,一看开着的两个柜台都有人,这个时候他们就会在候客区等待(就是阻塞队列第5个参数),这个时候又来了一个人,因为1,2柜台都有人,候客区也满了,这个时候银行就会开启新的柜台(之前没开启的3,4,5可能就会开启),来处理业务,过了一会,1,2,3,4,5柜台都满了,候客区也满了,这时候又进来了一个人,这个时候就会启动拒绝策略了(第6个参数),拒绝策略共有四种,前面已经介绍过了。等到3,4,5柜台都没有人了,候客区也没有人了,3、4、5这几个临时柜台等待超过3这个数量,4这个时间单位的时间后,还没有人来,3、4、5柜台就会关闭窗口,关闭释放!

2.4、自定义线程池
import java.util.concurrent.*;
public class Test3 {
    public static void main(String[] args) {
        // 自定义线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,5,3, TimeUnit.SECONDS,  new LinkedBlockingDeque<Runnable>(3),
                                                                       Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); // 银行满了,好有人进来,不处理这个人的,抛出异常
        try {
            // 最大承载:队列 + max ,即 3 + 5
            // 超出就会抛出 RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
                threadPoolExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName()+" ok");
                    }
                });
            }
        }catch (Exception e ) {
            e.printStackTrace();
        }finally {
            threadPoolExecutor.shutdown();
        }
    }
}
2.5、聊聊关闭线程池

关闭线程池有两种方式:1)shotdown();2)shutdownNow()

  • shutdown:新传入的任务不在接收,但是目前所有的任务(所有线程中的任务+工作队列种的任务)还要执行完毕
  • shutdownNow:新传入的任务不再接收,目前的任务(所有线程中的任务)判断是否可以停止,如果可以停止,就结束任务,如果不能停止,就执行完在停止;工作队列中的任务直接丢弃。
2.6、小结和扩展

池的最大大小如何设置?
两种方法:

1、CPU 密集型:几核就是几,可以保证CPU的效率最高

2、IO 密集型 > 判断你的程序中十分消耗IO的线程// 获取CPU的核数

// 获取CPU的核数
System.out.println(Runtime.getRuntime().availableProcessors());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值