zoukankan      html  css  js  c++  java
  • 详解 线程池

    (请观看本人博文 —— 《详解 多线程》


    线程池

    概念

    装有一定线程对象容器
    程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
    而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
    线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
    在JDK5之前,我们必须手动实现自己的线程池,
    从JDK5开始,Java内置支持线程池的类 —— ExecutorService 类


    此外,线程池有两大优点

    优点:

    1. 线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用
    2. 可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃

    那么,现在,本人来讲解下线程池的底层实现原理

    底层实现原理:

    流程:

    提交一个任务到线程池中,线程池的处理流程如下:

    1. 判断线程池里的核心线程是否都在执行任务
      如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务
      如果(核心线程都在执行任务),则进入下个流程
    2. 判断线程池的工作队列是否已满
      如果工作队列没有满,则将新提交的任务存储在这个工作队列里
      如果工作队列满了,则进入下个流程
    3. 判断线程池里的线程是否都处于工作状态
      如果没有满,则创建一个新的工作线程来执行任务
      如果已经满了,则交给饱和策略来处理这个任务

    如下图:
    在这里插入图片描述
    那么,现在,本人来讲解下 上文所提及的 饱和策略

    饱和策略:

    队列线程池了,说明线程池处于饱和状态
    那么必须对新提交的任务采用一种特殊的策略来进行处理。
    这个策略默认配置是AbortPolicy,表示无法处理新的任务而抛出异常
    对于上述情况,Java提供了4中策略:

    1. AbortPolicy:直接抛出异常
    2. CallerRunsPolicy:只用调用所在的线程运行任务
    3. DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
    4. DiscardPolicy:不处理,丢弃掉

    那么,接下来,本人就来讲解下与线程池相关的两个类吧:

    我们想要获得 ExecutorService 类 的对象,就只能通过 调用Executors 工厂类的方法来获取,所以,本人先来简单介绍下 Executors 工厂类

    Executors 工厂类:

    对于 Executors 工厂类,我们只需了解这个类是如何产生 ExecutorService类的对象即可。
    所以,本人先来介绍下这个类的常用API
    常用API

    • public static ExecutorService newCachedThreadPool():
      根据任务的数量来创建线程对应的线程个数
    • public static ExecutorService newFixedThreadPool(int nThreads):
      固定初始化几个线程
    • public static ExecutorService newSingleThreadExecutor():
      初始化一个线程线程池

    那么,现在,本人来介绍下 ExecutorService 类

    ExecutorService 类:

    对象的获得方法,在上面本人已经讲解过了。

    那么,现在,本人来展示下这个类的常用API

    常用API

    • < T > Future< T > submit(Callable< T > task)
      提交一个值返回任务执行,并返回一个表示任务挂起结果的未来
    • Future<?> submit(Runnable task)
      提交执行一个Runnable任务并返回一个表示该任务的未来
    • < T > Future< T > submit(Runnable task, T result)
      提交执行一个Runnable任务并返回一个表示该任务的未来
    • boolean isShutdown()
      返回 true如果执行器已关闭
    • boolean isTerminated()
      返回 true如果所有任务都完成后,关闭
    • void shutdown()
      启动一个有序的关机,在以前提交的任务被执行,但没有新的任务将被接受
    • List< Runnable > shutdownNow()
      试图阻止所有积极执行任务,停止等待任务的处理,并返回一个列表,正在等待执行的任务
    • boolean awaitTermination(long timeout, TimeUnit unit)
      直到所有的任务都完成后,关闭请求,或超时发生,或当前线程被中断,以先发生的情况
    • < T > List<Future< T >> invokeAll(Collection<? extends Callable< T >> tasks)
      执行给定的任务,返回一个未来持有他们的状态和结果的列表时,所有的完整
    • < T > List<Future< T >> invokeAll(Collection<? extends Callable< T >> tasks, long timeout, TimeUnit unit)
      执行给定的任务,返回一个未来持有他们的状态和结果的列表时,所有的完成或超时到期,以先发生的
    • < T > T invokeAny(Collection<? extends Callable< T >> tasks)
      执行给定的任务,返回已成功完成的结果(即,不抛出一个例外),如果任何
    • < T > T invokeAny(Collection<? extends Callable< T >> tasks, long timeout, TimeUnit unit)
      执行给定的任务,返回一个成功完成的结果(即,不抛出异常),如果做了超时之前经过

    对于上述的API ,本人现在来做几点说明

    1. 每次我们使用完后,一定要记得调用shutdown()方法,
      否则线程不会关闭、程序不会停止运行
    2. 只需将线程作为submit()的参数传入,就会有线程池中的线程来为完成该“任务

    现在,本人来通过几个例子,来展示下这个类的基本使用:

    本人首先来给出一个 Runnable的实现类 和 一个Callable的实现类:

    package edu.youzg.about_synchronized.core;
    
    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"任务执行了");
        }
    
    }
    
    package edu.youzg.about_thread.core;
    
    import java.util.concurrent.Callable;
    
    import static java.lang.Thread.sleep;
    
    public class MyCallable implements Callable<Integer>  {
        int num;
        public MyCallable(int num) {
            this.num=num;
        }
    
        @Override
        public Integer call() throws Exception {
            int sum=0;
            for (int i = 1; i <=num; i++) {
                sum += i;
            }
            return sum;
        }
    
    }
    

    例1 —— 使用无参构造获取线程池:

    package edu.youzg.about_thread.core;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Test {
    
       public static void main(String[] args){
           ExecutorService executorService = Executors.newCachedThreadPool();   //通过无参构造出来的线程池,容量是随着任务的数量改变的
           MyRunnable myRunnable = new MyRunnable();
           executorService.submit(myRunnable);
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           //关闭线程池
           executorService.shutdown();
       }
    
    }
    

    本人现在来展示下运行结果
    在这里插入图片描述
    可以看到:我们提交多少个“任务”,线程池中就会提供多少个线程来处理这些“任务

    例2 —— 使用有参构造获取线程池:

    package edu.youzg.about_thread.core;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Test {
    
       public static void main(String[] args){
           //这个线程里面,提前创建三个线程对象
           ExecutorService executorService = Executors.newFixedThreadPool(3);
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           //关闭线程池
           executorService.shutdown();
       }
    
    }
    

    本人来展示下运行结果
    在这里插入图片描述
    可以看到:线程池中只创建了三个线程,随机来完成传入的“任务


    例3 ——

    package edu.youzg.about_thread.core;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Test {
    
       public static void main(String[] args){
           //这个线程池里面,只有一个线程对象
           ExecutorService executorService = Executors.newSingleThreadExecutor();
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
           executorService.submit(new MyRunnable());
    
           executorService.shutdown();
       }
    
    }
    

    现在,本人来展示下运行结果
    在这里插入图片描述
    能够看到,线程池中只创建了一个线程。


    例4 —— 提交Callable实现类的任务作为submit()的参数:

    package edu.youzg.about_thread.core;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class Test {
    
       public static void main(String[] args) throws ExecutionException, InterruptedException {
           ExecutorService executorService = Executors.newFixedThreadPool(3);
           MyCallable myCallable = new MyCallable(10);
    
           Future<Integer> f = executorService.submit(myCallable);
           Integer integer = f.get();
           System.out.println(integer);
    
           MyCallable myCallable2 = new MyCallable(100);
    
           Future<Integer> f2 = executorService.submit(myCallable2);
           Integer integer1 = f2.get(); //获取线程执行完之后的返回结果
           System.out.println(integer1);
           executorService.shutdown();
       }
    
    }
    

    现在,本人再来展示下运行结果
    在这里插入图片描述

    可以看到:
    若传入的参数若是Runnable线程,就会运行完run()中的内容;
    若传入的参数若是Callable线程,不仅就会运行完run()中的内容,还可以获取返回值。

    我们所传的线程参数,根据我们的需要来传入即可。


    基本实现:

    请观看本人博文 —— 《详解 线程池 的基本实现》


    (本人 《详解 多线程》 博文 链接:https:////www.cnblogs.com/codderYouzg/p/12418935.html

  • 相关阅读:
    大战设计模式【13】—— 组合模式
    大战设计模式【12】—— 迭代器模式
    大战设计模式【11】—— 模板方法模式
    大战设计模式【10】—— 外观模式
    linux命令进阶
    ansible普通用户su切换
    Ansible 进阶技巧
    ansible playbook对错误的处理
    ansible示例,离线安装etcd
    (原)centos7安装和使用greenplum4.3.12(详细版)
  • 原文地址:https://www.cnblogs.com/codderYouzg/p/12418985.html
Copyright © 2011-2022 走看看