Java多线程之线程池概述

Java多线程之线程池

 

适用情况

 

  • 单个处理时间比较短
  • 将要处理的任务量比较大

 

 

好处

 

  • 线程重复使用
  • 减少多次创建和注销线程的开销

工作模型

扫描二维码关注公众号,回复: 528084 查看本文章


 

    1. 线程池的工作模型主要两部分组成,一部分是运行Runnable的Thread对象,另一部分就是阻塞队列。

 

    2. 由线程池创建的Thread对象其内部的run方法会通过阻塞队列的take方法获取一个Runnable对象,然后执行这个Runnable对象的run方法(即,在Thread的run方法中调用Runnable对象的run方法)。

 

    3. 当Runnable对象的run方法执行完毕以后,Thread中的run方法又循环的从阻塞队列中获取下一个Runnable对象继续执行。

 

    4. 当需要向线程池提交任务时会调用阻塞队列的offer方法向队列的尾部添加任务。提交的任务实际上就是是Runnable对象或Callable对象。

 

 

 

 

线程池工作流程

  

 

  1. 核心线程数,在创建线程池的时候还没有初始化
  2. 提交任务时,先判断核心线程数是否已经到达上限,如果不是就创建线程
  3. 如果工作线程数大于核心线程数,且任务队列未满,就进入任务队列
  4. 如果工作线程数大于核心线程数,且任务队列已满,那看工作线程数是否小于最大线程数,如果小于则创建线程
  5. 如果工作线程等于最大线程数,就会通过Handler所指定的策略来执行任务

上面的工作流程是根据ThreadPoolExecutor而言的,其实线程池的工作流程还要根据选择的线程池种类而定。



 

 

参数配置

corePoolSize:核心线程数

 

  • 核心线程会一直存活,及时没有任务需要执行
  • 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
  • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

 

queueCapacity:任务队列容量(阻塞队列)

 

  • 当核心线程数达到最大时,新任务会放在队列中排队等待执行

 

maxPoolSize:最大线程数

 

  • 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
  • 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

 

keepAliveTime:线程空闲时间

 

  • 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
  • 如果allowCoreThreadTimeout=true,则会直到线程数量=0

 

allowCoreThreadTimeout:允许核心线程超时

 

  • 这个值只设置true,false

 

rejectedExecutionHandler:任务拒绝处理器

 

下面两种情况会拒绝处理任务:

 

  • 当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务
  • 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务

几种拒绝任务的处理方式:

 

  • AbortPolicy(默认) 丢弃任务,抛运行时异常
  • CallerRunsPolicy 执行任务
  • DiscardPolicy 忽视,什么都不会发生
  • DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务

 

 

 

线程池种类

 

  • 固定数量线程池
  • 单一线程池:只有一个线程
  • 缓存线程池:线程池中的线程数量不固定,会根据需求改变
  • 计划任务调度线程池:用于执行计划,比如每五分钟执行一次
  • ThreadPoolExecutor

 

 

newFixedThreadPool

 

创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到任务队列中。

 

优缺点 

FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

 

代码

package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
 public static void main(String[] args) {
  ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
  for (int i = 0; i < 10; i++) {
   final int index = i;
   fixedThreadPool.execute(new Runnable() {
    public void run() {
     try {
      System.out.println(index);
      Thread.sleep(2000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    }
   });
  }
 }
}

 

 

 

newSingleThreadExecutor

 

创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。

 

优缺点
单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

 

代码

package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
 public static void main(String[] args) {
  ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
  for (int i = 0; i < 10; i++) {
   final int index = i;
   singleThreadExecutor.execute(new Runnable() {
    public void run() {
     try {
      System.out.println(index);
      Thread.sleep(2000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    }
   });
  }
 }
}

 

 

 

newCachedThreadPool

 

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

 

优缺点

  • 工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
  • 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
  • 在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。

代码

package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
 public static void main(String[] args) {
  ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  for (int i = 0; i < 10; i++) {
   final int index = i;
   try {
    Thread.sleep(index * 1000);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   cachedThreadPool.execute(new Runnable() {
    public void run() {
     System.out.println(index);
    }
   });
  }
 }
}

 

 

newScheduleThreadPool

 

创建一个定长的线程池,而且支持定时的以及周期性的任务执行。

 

延迟3秒执行,延迟执行示例代码如下:

package test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
 public static void main(String[] args) {
  ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
  scheduledThreadPool.schedule(new Runnable() {
   public void run() {
    System.out.println("delay 3 seconds");
   }
  }, 3, TimeUnit.SECONDS);
 }
}

 

表示延迟1秒后每3秒执行一次,定期执行示例代码如下:

package test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
 public static void main(String[] args) {
  ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
  scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
   public void run() {
    System.out.println("delay 1 seconds, and excute every 3 seconds");
   }
  }, 1, 3, TimeUnit.SECONDS);
 }
}

 

 

 

ThreadPoolExecutor

 

这是我们最常用的线程池,是线程池接口ExecutorService的实现类,其实上面的4中线程池都是由这个实现类来实现。

 

代码

public class DiscardPolicyDemo {  

    private static final int THREADS_SIZE = 1;  
    private static final int CAPACITY = 1;  

    public static void main(String[] args) throws Exception {  

        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。  
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(CAPACITY));  
        // 设置线程池的拒绝策略为"丢弃"  
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());  

        // 新建10个任务,并将它们添加到线程池中。  
        for (int i = 0; i < 10; i++) {  
            Runnable myrun = new MyRunnable("task-"+i);  
            pool.execute(myrun);  
        }  
        // 关闭线程池  
        pool.shutdown();  
    }  
}

 

 

 

线程池拒绝策略

 

  • AbortPolicy(默认):如不能接收任务,抛异常
  • CallerRunsPolicy:~~~~~~~~,让调用的线程去完成
  • DiscardOldestPolicy:~~~~~~,则丢弃最老一个任务,由一个队列来维护
  • DiscardPolicy:~~~~~~~~~~~,丢弃任务

设置线程池拒绝策略

// 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。  
ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(CAPACITY));  
// 设置线程池的拒绝策略为"丢弃"  
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

  

 

 

线程池优化需要考虑的因数

  • 并发数
  • 单次请求的执行时间

线程池初始化

  • 默认新创建线程池中没有线程,有任务后才创建
  • prestartCoreThread()初始化一个核心线程
  • prestartAllCoreThread()初始化所有核心线程

线程池关闭

  • shutdown(),等任务执行完再终止,不接受新任务
  • shutdownNow(),马上终止,打断正在执行的任务,清空缓存队列,返回未执行的任务

 

猜你喜欢

转载自youyu4.iteye.com/blog/2351551