线程池

线程池

一、线程池的优势

方式:

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

特点:
  1. 线程复用
  2. 控制最大并发数
  3. 管理线程
优势:
  1. 降低资源消耗。通过重复使用已创建的线程降低线程创建和销毁所带来的消耗
  2. 提高响应速度。当任务达到时,可以不需要等到线程创建就能立即执行
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅消耗资源,并且还会降低系统的稳定性,使用线程池可以进行统一分配,调优,监控

二、创建线程池的方式

  1. Executors.newFixedThreadPool(int):创建一个固定长度线程的线程池

    执行长期任务,性能好很多

  2. Executors.newSingleThreadExecutor():创建一个单线程的线程池

    一个任务一个任务执行的场景

  3. Executors.newCachedThreadPool():创建一个可缓存的线程池,可根据处理需要自动创建N个线程

    执行很多短期异步的小程序或者负载较轻的服务器

  4. Executors.newScheduledThreadPool():创建一个可调度的线程池(了解)

  5. Executors.newWorkStealingPool():使用目前机器上可用的处理器作为它的并行级别(Java8新增)(了解)

package com.ctgu.juc;

import java.util.concurrent.*;

/**
 * 创建线程方式,通过线程池创建
 */
public class MyThreadPoolDemo {

    public static void main(String[] args) throws InterruptedException {

        //通过Executors线程池工具类创建一个线程池:一个池子一个线程
        ExecutorService threadPool1 = Executors.newSingleThreadExecutor();
        threadPoolTest(threadPool1);

        //通过Executors线程池工具类创建一个线程池:一个池子固定有5个线程
        ExecutorService threadPool2 =  Executors.newFixedThreadPool(5);
        threadPoolTest(threadPool2);

        //通过Executors线程池工具类创建一个线程池:一个池子N个线程
        ExecutorService threadPool3 =  Executors.newCachedThreadPool();
        threadPoolTest(threadPool3);

    }

    private static void threadPoolTest(ExecutorService threadPool) throws InterruptedException {

        //模拟10个用户办理业务,一个用户就是一个线程
        try{
            for (int i = 0; i < 10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t 办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }

    }

}

三、线程池的参数

三个创建线程池方法的源代码:
都是用ThreadPoolExecutor()传入参数创建线程池

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

三个线程池创建方法最终用该类构造方法:

ThreadPoolExecutor.java

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;
}
七大参数
  1. corePoolSize:线程池中的常驻核心线程数
  2. maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于1
  3. keepAliveTime:多余的空闲线程存活时间
  4. unit:keepAliveTime的单位
  5. workQueue:任务队列,被提交但尚未被执行的任务
  6. threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程一般默认的即可
  7. handler:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数时,如何拒绝请求执行的Runnable的策略

四、线程池的工作原理

线程池工作原理
线程池工作原理

  1. 在创建了线程池以后,等待提交过来的任务请求
  2. 当调用execute()方法添加一个请求任务的时候,线程池做一下判断:
    1. 如果正在运行的线程数量小于corePoolSize,那么马上创建线程执行这个任务
    2. 如果正在运行的线程数量大于等于corePoolSize,那么将这个队列放入任务队列
    3. 如果这时候任务队列也满了,且正在运行的线程还小于maximumPoolSize,那么创建非核心线程立刻执行这个任务
    4. 如果这时候队列满了,且正在运行的线程大于等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行
  3. 当一个线程完成任务时,它会从队列中取出一个任务来执行
  4. 当一个线程空闲超过一定时间时,线程会做以下判断:
    1. 如果当前运行线程大于corePoolSize,那么这个线程就会被停掉
    2. 所以线程池的所有任务完成后它最终会收缩到corePoolSize这个大小

五、拒绝策略

AbortPolicy(默认):直接抛出RejectExecutionException异常阻止系统正常运行

CalerRunsPolicy:既不会抛弃任务,也不会抛异常,而是将某些任务回退到调用者,从而降低新任务的流量

DiscardOldestPolicy:丢弃队列中等待最久的任务,然后把当前任务加入队列尝试再次提交当前任务

DiscardPolicy:直接丢弃任务,不予处理也不抛异常。如果允许丢失,这是最好的一种方案

六、使用线程池

一般不直接用Executors工具类创建线程池,因为它创建的线程池用的阻塞队列都是LinkedBlockingQueue,该阻塞队列默认是有界但是大小为Integer.MAX_VALUE,会造成并发问题

所以一般都是用 ThreadPoolExecutor 类手动创建线程池

package com.ctgu.juc;


import java.util.concurrent.*;

/**
 * 创建线程方式,通过线程池创建
 */
public class MyThreadPoolDemo {

    public static void main(String[] args) throws InterruptedException {
        
        ExecutorService executor = new ThreadPoolExecutor(
                2,		//corePoolSize
                5,		//maximumPoolSize
                1,		//keepAliveTime
                TimeUnit.SECONDS,		//unit
                new LinkedBlockingQueue<Runnable>(3),	//BlockingQueue
                Executors.defaultThreadFactory(),		//ThreadFactory
                new ThreadPoolExecutor.DiscardPolicy());//RejectedExecutionHandler

        //模拟10个用户办理业务,一个用户就是一个线程
        try{
            for (int i = 0; i < 10; i++) {
                executor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t 办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            executor.shutdown();
        }
    }

}

七、合理配置线程池

CPU密集型:该任务需要大量的运算,而没有阻塞,CPU一直全速运转。

CPU密集型任务配置尽可能少的线程数量

一般公式:CPU核数+1个线程的线程池

IO密集型:该任务需要大量的IO,即大量的阻塞。

IO密集型中大多数线程都阻塞,所以需要尽可能多的线程数

参考公式:CPU核数 / (1 - 阻塞系数) 阻塞系数一般在0.8~0.9之间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值