线程池用过吗?ThreadPoolExecutor谈谈你的理解

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

它的主要特点为:线程复用控制最大并发数管理线程

为什么要用线程池,优势:

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

线程池如何使用:

  • ExecutorService threadPool = Executors.newFixedThreadPool(3); 固定线程数的线程池。执行长期的任务,性能好很多
  • ExecutorService threadPool = Executors.newSingleThreadExecutor(); 单一线程的线程池。一个任务一个任务执行的场景
  • ExecutorService threadPool = Executors.newCachedThreadPool(); 可扩容可回收的线程池。执行很多短期异步的小程序或者负载较轻的服务器

这些只是Executors工具类的一些创建线程池方法,而我们在生产中只能使用自定义的线程池。

线程池的几个重要参数介绍:

  • corePoolSize:线程池中的常驻核心参数
  • maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于1
  • keepAliveTime:多余的空闲线程存活时间。当前线程池中线程数量超过corePoolSize值时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个为止
  • unit:keepAliveTime的单位
  • workQueue:任务队列,被提交但尚未执行的任务
  • threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般用默认的即可
  • handler:拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大线程数maximumPoolSize
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}

说说线程池的底层工作原理:

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




第三种获得多线程的方法:
Callable 接口
如何使用Callable创建一个线程: 通过FutureTask的构造方法
在这里插入图片描述
模拟做菜准备:买厨具、买菜。厨具是网购,买菜是自己去

public static void main(String[] args) {
    long start = System.currentTimeMillis();

    Thread tool = new Thread(() -> {
        try {
            System.out.println(Thread.currentThread().getName() + " 购买厨具");
            System.out.println(Thread.currentThread().getName() + " 厨具开始送货");
            TimeUnit.SECONDS.sleep(3);
            System.out.println(Thread.currentThread().getName() + " 厨具送到");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "toolThread");

    tool.start();

    //模拟等待送货时间,因为必须要得到厨具才能做饭,而送货时间是不确定的,送货线程也没有返回值,我们无法知道该什么时候去获取厨具,所以一直等他的结果
    try {
        tool.join();
        System.out.println("获得厨具:"+ "厨具");
        System.out.println("shopThread开始去买菜");
        //买菜时间
        TimeUnit.SECONDS.sleep(3);
        System.out.println("shopThread买菜完毕");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("准备工作完毕");

    long end = System.currentTimeMillis();
    System.out.println("共用时:" + (end-start) / 1000 + "s");
}


toolThread 购买厨具
toolThread 厨具开始送货
toolThread 厨具送到
获得厨具:厨具
shopThread开始去买菜
shopThread买菜完毕
准备工作完毕
共用时:6s

这两个过程应该可以同时分开进行

public static void main(String[] args) {
    long start = System.currentTimeMillis();

    FutureTask<String> toolTask = new FutureTask(() -> {
        try {
            System.out.println(Thread.currentThread().getName() + " 购买厨具");
            System.out.println(Thread.currentThread().getName() + " 厨具开始送货");
            TimeUnit.SECONDS.sleep(3);
            System.out.println(Thread.currentThread().getName() + " 厨具送到");
            return "厨具";
        } catch (InterruptedException e) {
            throw new InterruptedException();
        }
    });

    new Thread(toolTask, "toolThread").start();

    try {
        System.out.println("shopThread开始去买菜");
        //买菜时间
        TimeUnit.SECONDS.sleep(3);
        System.out.println("shopThread买菜完毕");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    try {
        String tool = toolTask.get(); //获取异步线程工作的返回值,如果还没执行完会造成阻塞,所以尽量放在最后
        //可以用 toolTask.isDOne() 判断是否执行完成
        System.out.println("获得厨具:" + tool);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    System.out.println("准备工作完毕");

    long end = System.currentTimeMillis();
    System.out.println("共用时:" + (end-start) / 1000 + "s");

}


shopThread开始去买菜
toolThread 购买厨具
toolThread 厨具开始送货
shopThread买菜完毕
toolThread 厨具送到
获得厨具:厨具
准备工作完毕
共用时:3s

拥有相同FutureTask的线程启动,只会运行一次,它的结果可以复用

new Thread(toolTask, "AA").start();
new Thread(toolTask, "BB").start();


第四种获得多线程的方法,线程池:

Java中的线程池是通过Executor框架实现的,该框架中用到了Executor, Executors. ExecutorService, ThreadPoolExecutor 这几个类在这里插入图片描述

编码实现:

  • (了解)Executoes.newScheduledThreadPool()
  • (了解 java8新出)Executors.newWorkStealingPool(int) 使用目前机器上可用的处理器作为他的并行级别
  • ExecutorService executorService = Executors.newFixedThreadPool(3); 固定线程数的线程池。执行长期的任务,性能好很多
  • ExecutorService executorService = Executors.newSingleThreadExecutor(); 单一线程的线程池。一个任务一个任务执行的场景
  • ExecutorService executorService = Executors.newCachedThreadPool(); 可扩容可回收的线程池。执行很多短期异步的小程序或者负载较轻的服务器
public static void main(String[] args) {
    ExecutorService threadPool = Executors.newFixedThreadPool(3);

    try {
        //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
        for (int i = 0; i < 6 ; i++) {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " doing job");
                try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPool.shutdown();
    }
}


pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
public static void main(String[] args) {
    ExecutorService threadPool = Executors.newSingleThreadExecutor();

    try {
        for (int i = 0; i < 6 ; i++) {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " doing job");
                try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPool.shutdown();
    }
}


pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
public static void main(String[] args) {
    ExecutorService threadPool = Executors.newCachedThreadPool();

    try {
        for (int i = 0; i < 6 ; i++) {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " doing job");
                try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPool.shutdown();
    }
}


pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
pool-1-thread-4 doing job
pool-1-thread-5 doing job
pool-1-thread-6 doing job

这三个线程池构建方法的底层: 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 newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}    
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值