Java教程

六、Java线程池

本文主要是介绍六、Java线程池,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

    • 6.1为什么用线程池?
    • 6.2线程池如何使用
      • 6.2.1线程池的架构说明
      • 6.2.2创建线程池3种
      • 6.2.3ThreadPoolExecutor底层原理
    • 6.3线程池7个重要的参数
    • 6.4线程池底层工作原理
    • 6.5线程池用哪个?生产中如何设置合理参数?
      • 6.5.1线程池的拒绝策略
      • 6.5.2三种创建线程池的方法那个用的多?都不用
      • 6.5.3自定义线程池

6.1为什么用线程池?

image-20210929154759040

6.2线程池如何使用

image-20211007124010272

线程池的工具类:

  • Arrays:数组的工具类
  • Collections:集合的工具类
  • Executors:线程池的工具类

6.2.1线程池的架构说明

Java中的线程池是通过Executor框架实现的,该框架中用到了Executor、Executors、 ExecutorService、 ThreadPoolExecutor这几个类

image-20211007132729985

6.2.2创建线程池3种

  • Executors.newFixedThreadPool(int) 创建指定线程数量的线程池

  • Executors.newSingleThreadExecutor() 创建只有一个线程的线程池

  • Executors.newCachedThreadPool() 根据需要创建新的线程,可扩容

Executors.newFixedThreadPool(int)

public class MyThreadDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5); //一池5个工作线程,类似一个银行有5个受理窗口
       try {
           //模拟有10个顾客过来银行办理业务,目前池子里面有5个工作人员提供服务
           for (int i = 1; i < 10; i++) {
               threadPool.execute(()->{
                   System.out.println(Thread.currentThread().getName()+" 办理业务");
               });
           }
       }catch (Exception e){
           e.printStackTrace();
       }finally {
            threadPool.shutdown();
       }

    }
}
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-4 办理业务
pool-1-thread-2 办理业务
pool-1-thread-2 办理业务
pool-1-thread-5 办理业务
pool-1-thread-3 办理业务
pool-1-thread-4 办理业务
pool-1-thread-1 办理业务

注意:线程池的名字自己有

Executors.newSingleThreadExecutor()

public class MyThreadDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor(); //y一池1个工作线程,类似一个银行有1个受理窗口
        try {
           //模拟有10个顾客过来银行办理业务,目前池子里面有1个工作人员提供服务
           for (int i = 1; i < 10; i++) {
               threadPool.execute(()->{
                   System.out.println(Thread.currentThread().getName()+" 办理业务");
               });
           }
       }catch (Exception e){
           e.printStackTrace();
       }finally {
            threadPool.shutdown();
       }

    }
}
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务

Executors.newFixedThreadPool(int) 问题:假设创建了拥有5个线程的线程池,如果只有2个线程来访问则浪费

Executors.newSingleThreadExecutor() 问题:假设创建了拥有1个线程的线程池,如果有多个线程来访问则造成浪费

Executors.newCachedThreadPool()可以解决上述问题,动态创建

Executors.newCachedThreadPool()

public class MyThreadDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool(); //一池N个工作线程,类似一个银行有N个受理窗口
        try {
           //模拟有10个顾客过来银行办理业务,目前池子里面有N个工作人员提供服务
           for (int i = 1; i < 10; i++) {
               threadPool.execute(()->{
                   System.out.println(Thread.currentThread().getName()+" 办理业务");
               });
           }
       }catch (Exception e){
           e.printStackTrace();
       }finally {
            threadPool.shutdown();
       }

    }
}
pool-1-thread-2 办理业务
pool-1-thread-1 办理业务
pool-1-thread-3 办理业务
pool-1-thread-4 办理业务
pool-1-thread-5 办理业务
pool-1-thread-1 办理业务
pool-1-thread-3 办理业务
pool-1-thread-2 办理业务
pool-1-thread-4 办理业务

6.2.3ThreadPoolExecutor底层原理

image-20211007140441537

image-20211007140453590

image-20211007140504905

6.3线程池7个重要的参数

image-20211007141208453
  1. corePoolSize:线程池中的常驻核心线程数,线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut
  2. maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1
  • CPU 密集型程序:maximumPoolSize设置成核心数 Runtime.getRuntime().availableProcessors()获取核心数
  • I/O 密集型程序:maximumPoolSize设置成CPU核心数的两倍最佳
  1. keepAliveTime:多余的空闲线程的存活时间,当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁直到只剩下corePoolSize个线程为止
  2. unit :空闲线程存活时间单位 keepAliveTime的计量单位
  3. workQueue :任务队列,被提交但尚未被执行的任务
  4. threadFactory :线程工厂创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等
  5. handler :拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝请求执行的runnable的策略

解释:

假设一个银行网点常驻办理窗口3个(corePoolSize),如果突然很多顾客来办理业务,将无法及时办理业务的顾客让其在等候区排队(workQueue),如果等候区也满了,将窗口增加至5个(maximumPoolSize)。如果5个窗口满了而且等候区也满了,则关门拒绝提供服务(handler)。假如一段时间内(keepAliveTime)都只有少于3个顾客,此时3个窗口已经够用,将5个窗口减少至3个,也就是空闲线程失活。

6.4线程池底层工作原理

image-20211007145833931

6.5线程池用哪个?生产中如何设置合理参数?

6.5.1线程池的拒绝策略

是什么?

等待队列已经排满了,再也塞不下新任务了,同时线程池中的max线程也达到了,无法继续为新任务服务,这是就需要拒绝策略机制合理的处理这个问题。

JDK内置的拒绝策略

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

  • CallerRunsPolicy:调用者运行,一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。

  • DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常,如果允许任务的丢失,这是最好的一种策略。

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

6.5.2三种创建线程池的方法那个用的多?都不用

阿里巴巴开发手册

image-20211007153055177

6.5.3自定义线程池

当使用AbortPolicy拒绝策略时,如果线程数量超过maximumPoolSize+workQueue则报错

public class MyThreadDemo {
    public static void main(String[] args) {
        
        ThreadPoolExecutor threadPool= new ThreadPoolExecutor(2,5,2L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        try {
            for (int i = 0; i < 9; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" 办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

image-20211007155112134

当使用CallerRunsPolicy拒绝策略时,如果线程数量超过maximumPoolSize+workQueue则回退给调用者运行

public class MyThreadDemo {
    public static void main(String[] args) {
        
        ThreadPoolExecutor threadPool= new ThreadPoolExecutor(2,5,2L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
      .....
      省略,同上
    }
}

image-20211007160200086

当使用DiscardPolicy拒绝策略时,如果线程数量超过maximumPoolSize+workQueue则丢弃无法处理的线程

public class MyThreadDemo {
    public static void main(String[] args) {
        
        ThreadPoolExecutor threadPool= new ThreadPoolExecutor(2,5,2L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());
      .....
      省略,同上
    }
}

image-20211007160937604

只运行了8个任务,有一个任务被抛弃

当使用DiscardOldestPolicy拒绝策略时,如果线程数量超过maximumPoolSize+workQueue则抛弃队列中等待最久的任务

public class MyThreadDemo {
    public static void main(String[] args) {
        
        ThreadPoolExecutor threadPool= new ThreadPoolExecutor(2,5,2L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());
      .....
      省略,同上
    }
}

image-20211007161132118

这篇关于六、Java线程池的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!