【多线程】初识线程池,我悟了!

线程池的重点内容:

  1. 线程池的优点

  2. 线程池的七种创建方式

  3. ThreadPoolExecutor(线程数不可控,任务数不可控)

  4. ThreadPoolExecutor七个参数顺序和参数说明

    1. 核心线程数

    2. 最大线程数

    3. 生存时间

    4. 时间单位

    5. 任务队列

    6. 线程工厂(优先级 命名 类型)

    7. 拒绝策略

  5. ThreadPoolExecutor拒绝策略(五种)

    1. 默认拒绝策略(不执行任务,直接抛出异常)

    2. 使用调用者线程执行任务(使用主线程来执行任务)

    3. 忽略最新任务(不会报错)

    4. 舍弃老任务

    5. 自定义拒绝策略

  6. 线程池的状态和停止方法

线程缺点    

  1. 线程的创建会开辟本地方法栈、虚拟机栈、程序计数器线程私有的内存,同时销毁的时候会消耗以上三个区域,因此频繁的创建和销毁比较消耗系统资源
  2. 在任务量远远大于线程可以处理的任务量的时候,并不能友好拒绝任务

引出线程池:

    定义:使用池化技术来管理线程和使用线程的方式
    优点:
        1. 避免频繁的创建和销毁线程(复用线程)
        2.可以更好的管理线程的数量和资源的数量(任务队列)
        3.线程池拥有更多的功能:线程池可以进行定时任务的执行
        4.使用线程池可以更友好的拒绝不能处理的任务
 

java.util.concurrent

并发类都会放在juc下

线程池的创建方式分为七种:

    线程池第一种创建方式:创建固定个数的线程池(不推荐使用,任务队列为Integer.MAX_VALUE,线程数不可控,可能会造成内存溢出)
public class ThreadPoolDemo45 {
    public static void main(String[] args) {

        // 创建固定个数的线程池
        ExecutorService service = Executors.newFixedThreadPool(1);
        // 定义任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程名:" +
                        Thread.currentThread().getName());
            }
        };
        // 执行任务
        service.execute(runnable);
        service.execute(runnable);
        service.execute(runnable);
        service.execute(runnable);
        service.execute(runnable);
        service.execute(runnable);
        service.execute(runnable);
        service.execute(runnable);
        service.execute(runnable);
        service.execute(runnable);
    }
}

 

    线程池第二种创建方式:创建带缓存的线程池:
        使用场景:短期有大量任务的时候使用 newCachedThreadPool(根据任务数生成对应的线程数,所以适用于短期大量任务)
public class ThreadPoolDemo47 {
    public static void main(String[] args) {
        // 创建带缓存的线程池
        ExecutorService service = Executors.newCachedThreadPool();
        // 执行 10 个任务
        for (int i = 0; i < 100; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:" +
                            Thread.currentThread().getName());
                }
            });
        }

    }
}

 

 
    线程池第三种创建方式:创建可以执行定时任务的线程池
public class ThreadPoolDemo49 {
    public static void main(String[] args) {
        // 创建一个执行定时任务的线程池
        ScheduledExecutorService service =
                Executors.newScheduledThreadPool(10);
        System.out.println("执行任务之前:" + new Date());
        // 执行任务
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:" + new Date());
            }
        }, 3, TimeUnit.SECONDS);
    }
}

 

参数1:线程执行的任务 Runnable
参数2:延迟一段时间执行
参数3:定时任务执行频率
参数4:配合参数2、3使用,规定时间的单位
public class ThreadPoolDemo50 {
    public static void main(String[] args) {
        // 创建一个执行定时任务的线程池
        ScheduledExecutorService service =
                Executors.newScheduledThreadPool(10);
        // 执行任务
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:" + new Date());
            }
        }, 1, 3, TimeUnit.SECONDS);
    }
}

 

只会执行一次
 
 
scheduleAt...:以上次任务开始时间+时间间隔作为下一次开始时间
scheduleWith:以上次任务执行结束时间作为下一次开始时间
 
线程池第四种创建方式:创建单线程执行定时任务的线程池
第三种创建方式的单个线程版本
public class ThreadPoolDemo51 {
    public static void main(String[] args) {
        // 创建单个执行定时任务的线程池
        ScheduledExecutorService service =
                Executors.newSingleThreadScheduledExecutor();
        // 开启定时任务
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:" + new Date());
            }
        }, 1, 3, TimeUnit.SECONDS);
    }
}

 

线程池第五种创建方式:创建单个线程的线程池
第一种创建方式的单个线程版本
 
PS:面试题:创建单个线程的线程池有什么用?
    1.使用池化技术,可以避免频繁创建和销毁线程带来的性能开销
    2.线程池中都有一个任务队列,有任务队列可以存储多余的任务(更好地分配和执行任务)
    3.当有大量的任务不能处理的时候,线程池可以自定义拒绝策略,友好的执行拒绝策略
    4.线程池可以更好的管理任务
线程池第六种创建方式:JDK8以上版本
根据当前的CPU核心数,任务量生成对应个数的线程池,并且是异步执行
        同步执行和异步请求
        同步:

1.发请求

2.等待执行完成

3.结果返回

        异步:不会等待处理

1.发请求

2.执行完成 

3.另一个程序异步处理

4.处理完成后调用回调函数返回结果

 
线程池第七种创建方式:使用ThreadPoolExecutor方式创建

ThreadPoolExecutor七个参数顺序和参数说明

  1. 核心线程数

  2. 最大线程数

  3. 生存时间

  4. 时间单位

  5. 任务队列

  6. 线程工厂(优先级 命名 类型)

  7. 拒绝策略

 

ThreadPoolExecutor执行流程

线程池里有两个重要的对象:

    1.线程

    2.工作(任务)队列(工作队列的长度为Integer.MAX_VALUE)  

   

如果使用前六种使用Executors创建线程池的方式会导致的问题:

    1.线程数量不可控(比如创建带缓存的线程池)

    2.工作任务量不可控(Integer.MAX_VALUE),可能会导致内存溢出

 

拒绝策略

JDK提供4种拒绝策略 + 自定义拒绝策略
 
1.默认的拒绝策略
 
2.使用调运线程池的
        
3.忽略新任务
 
4.忽略老任务
 
5.自定义拒绝策略(重写拒绝方法)
 

线程池关闭

1.shutdown()

    调用shutdown()后会拒绝执行新任务,等待线程池中的任务队列执行完之后再停止线程池

2.shutdownNow()

    调用shutdownNow()后会拒绝执行新任务,不会等待任务队列的任务执行完成,就停止线程池

线程池状态(5个):

    不等于线程状态(6种)

private static final int RUNNING = -1 << COUNT_BITS;(不管有没有任务都是running)

private static final int SHUTDOWN = 0 << COUNT_BITS;(调用shutdown())

private static final int STOP = 1 << COUNT_BITS;(调用shutdownNow())

private static final int TIDYING = 2 << COUNT_BITS;(pool是空的时候)

private static final int TERMINATED = 3 << COUNT_BITS;(销毁状态)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_RailGun_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值