My线程池笔记

线程池的概念

线程池,一种多线程的处理形式,处理过程中可以将任务添加到队列中,然后创建线程启动这些任务。 这里的任务就是实现了Runnable或Callable接口或继承Thread类的对象实例(通俗讲就是需要执行的代码逻辑)。

线程池属于JUC( java.util.concurrent )包。

使用线程池好处:

  1. 重复利用已有的线程继续执行任务,避免线程在创建和销毁时造成的消耗。
  2. 由于没有创建和销毁时造成的消耗,因此可以提高系统响应速度。
  3. 可以对线程进行合理的管理,根据系统的承受能力调整可运行线程数量的大小等。

例子: 线程池中已有10个已经就绪的线程,此时有100个任务等待执行。
那么系统就会先分配10个已就绪的线程去处理其中的10个任务,剩下90个任务暂时等待。待正在运行的线程空闲后,立马又去执行其他的任务。这样就避免了线程创建和销毁时造成的消耗。因为线程池中的线程重复利用了,自然不需要再创建线程了。

线程池的分类和作用:

1、newCachedThreadPool (可缓存)

作用:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程。

特征:
(1)线程池中数量没有固定,可达到最大值(Interger. MAX_VALUE)
(2)线程池中的线程可进行缓存重复利用和回收(回收默认时间为1分钟)
(3)当线程池中,没有可用线程,会重新创建一个线程

创建方式: Executors.newCachedThreadPool();

2、newFixedThreadPool (定长)

作用:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。

特征:
(1)线程池中的线程处于一定的量,可以很好的控制线程的并发量
(2)线程可以重复被使用,在显示关闭之前,都将一直存在
(3)超出一定量的线程被提交时候需在队列中等待

创建方式:
(1)Executors.newFixedThreadPool(int nThreads);//nThreads为线程的数量
(2)Executors.newFixedThreadPool(int nThreads,ThreadFactory threadFactory);//nThreads为线程的数量,threadFactory创建线程的工厂方式

3、newSingleThreadExecutor (单例)

作用:创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。

特征:
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行

创建方式:
(1)Executors.newSingleThreadExecutor() ;
(2)Executors.newSingleThreadExecutor(ThreadFactory threadFactory);// threadFactory创建线程的工厂方式

4、newScheduleThreadPool (定时)

作用: 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

特征:
(1)线程池中具有指定数量的线程,即便是空线程也将保留
(2)可定时或者延迟执行线程活动

创建方式:
(1)Executors.newScheduledThreadPool(int corePoolSize);// corePoolSize线程的个数
(2)newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory);// corePoolSize线程的个数,threadFactory创建线程的工厂

5、newSingleThreadScheduledExecutor

作用: 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。

特征:
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
(2)可定时或者延迟执行线程活动

创建方式:
(1)Executors.newSingleThreadScheduledExecutor() ;
(2)Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory) ;//threadFactory创建线程的工厂

并发队列

非阻塞队列:
当队列中数据数量已达到队列容器大小时,再执行入队操作将造成数据丢失。

当队列中没有数据时,执行出对操作,取出的数据为null。
阻塞队列
当队列中数据数量已达到队列容器大小时,再执行入队操作,数据将在容器外等待,等到队列有数据出对时,再插入数据。

当队列中没有数据时,执行出对操作,将等待,直到有数据入队。

线程池中使用的正是阻塞队列。

ThreadPoolExecutor

在这里插入图片描述
corePoolSize: 核心线程数,线程池的基本大小。
(1)核心线程会一直存在,即使没有任务执行;
(2)当线程数小于核心线程数的时候,即使有空闲线程,也会一直创建线程直到达到核心线程数;
(3)设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。

maximumPoolSize:最大线程数,线程池中能够容纳的最大线程数量
如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务

keepAliveTime: 线程空闲时间
(1)当线程空闲时间达到keepAliveTime时,线程会退出(关闭),直到线程数等于核心线程数;
(2)如果设置了allowCoreThreadTimeout=true,则线程会退出直到线程数等于零。

unit: 线程空闲时间单位

BlockingQueue workQueue: 阻塞队列

其中还有
allowCoreThreadTimeout: 允许核心线程超时
rejectedExecutionHandler: 任务拒绝处理器
threadFactory: 用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程做些更有意义的事情,比如设置daemon和优先级等等
(1)当线程数量达到最大线程数,且任务队列已满时,会拒绝任务;
(2)调用线程池shutdown()方法后,会等待执行完线程池的任务之后,再shutdown()。如果在调用了shutdown()方法和线程池真正shutdown()之间提交任务,会拒绝新任务。

线程池工作流程:

在这里插入图片描述

搞懂线程池工作流程

首先创建一个线程任务

class TestThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

main函数中创建线程池

//创建线程池
        ThreadPoolExecutor pool =
                new ThreadPoolExecutor(1,2,3, TimeUnit.SECONDS,new LinkedBlockingDeque<>(3));

核心线程数为1,最大线程数为2,线程空闲时间为3,单位为秒,阻塞队列容量为3

1.执行第一个任务:

pool.execute(new TestThread());

结果为:
在这里插入图片描述
任务被线程1执行,此时的任务是由核心线程在处理。

2.执行第二、三、四个任务

//执行第一个任务
pool.execute(new TestThread());

//执行第二、三、四个任务
pool.execute(new TestThread());
pool.execute(new TestThread());
pool.execute(new TestThread());

结果:
在这里插入图片描述
核心线程执行一个任务,剩下的任务进入阻塞队列中等待处理,因此全部都由核心线程执行任务。

3.执行第五个任务

        //执行第一个任务
        pool.execute(new TestThread());

        //执行2,3,4,任务
        pool.execute(new TestThread());
        pool.execute(new TestThread());
        pool.execute(new TestThread());

        //执行第五个任务
        pool.execute(new TestThread());

结果:
在这里插入图片描述
或者
在这里插入图片描述
执行第五个任务时,阻塞队列已满,因此再创建一个线程去执行第五个任务。由于核心线程和创建的新线程谁先空闲不确定,因此剩余的任务将由核心线程和创建的新线程共同分摊处理。就会出现如上图的随机输出,多次执行程序效果更明显。

4.执行第六个任务

        //执行第一个任务
        pool.execute(new TestThread());

        //执行2,3,4,任务
        pool.execute(new TestThread());
        pool.execute(new TestThread());
        pool.execute(new TestThread());

        //执行第五个任务
        pool.execute(new TestThread());

        //执行第六个任务(报错)
        pool.execute(new TestThread());

结果:
在这里插入图片描述
出现错误信息。原因在于,当执行第五个任务时新创建了一个线程,线程池中有2个线程,达到了最大线程数。此时阻塞队列还是满的,再加入一个任务,需要再创建一个新线程去处理,因此就超过了最大线程数,就报错啦。

拒绝策略

AbortPolicy
默认饱和策略。当线程池饱和时使用该策略,会抛出RejectExecutionException异常。

DiscardPolicy
丢弃无法加载的任务,啥也不做。

DiscardOldestPolicy
将阻塞队列中的头元素出队抛弃,再尝试提交任务。如果此时阻塞队列使用

PriorityBlockingQueue 优先级队列,将会导致优先级最高的任务被抛弃,因此不建议将该种策略配合优先级队列使用。

CallerRunsPolicy
既不抛弃任务也不抛出异常,直接运行任务的run方法,换言之将任务回退给调用者来直接运行。使用该策略时,线程池饱和后将由调用线程的主线程自己来执行任务,因此在执行任务的这段时间里,主线程无法再提交新任务,从而使线程池中工作线程有时间将正在处理的任务处理完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值