详解 线程池

目录

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


@


线程池

概念

装有一定线程对象容器
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在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

上一篇:4. Spark 提交应用


下一篇:Java线程池之WorkStealingPool,任务窃取算法