线程池原理及常用线程池介绍

线程池

    • 线程池概述
      • 1.线程池的好处
      • 2.线程池的图解
      • 3.线程池的运行过程
    • 线程池解析
      • 1. 阻塞队列
      • 2. handler:拒绝处理策略
      • 3. 如何设置线程池参数(仅供参考)
    • 常用的线程池
      • 1. newCachedThreadPool()
      • 2 . newFixedThreadPool(int nThreads)
      • 3. newSingleThreadExecutor()

线程池概述

就是把一堆线程创建好了放在一个容器中(池子里),需要用的时候就直接拿出来用,用完之后再放回池子里。

1.线程池的好处

  1. 降低资源消耗,降低了频繁创建线程和销毁线程的开销
  2. 提高响应速度
  3. 提高线程的可管理性,可以对线程进行一些操作,方便管理线程

2.线程池的图解

线程池原理及常用线程池介绍_第1张图片

3.线程池的运行过程

线程池原理及常用线程池介绍_第2张图片

线程池解析

线程池的最上层接口是Executor,这个接口定义了一个核心方法execute(Runnabel command),这个方法最后被ThreadPoolExecutor类实现,这个方法是用来传入任务的。而且ThreadPoolExecutor是线程池的核心类,此类的构造方法如下:

线程池原理及常用线程池介绍_第3张图片

1. 阻塞队列

用来创建线程,一般有三种选择的阻塞队列:

  • ArrayBlockingQueue:有界队列(基于数组的)
  • LinkedBlockingQueue:有/无界队列(基于链表的,传参就是有界,不传就是无界)
  • SynchronousQueue:同步移交队列(需要一个线程调用put方法插入值,另一个线程调用take方法删除值)

2. handler:拒绝处理策略

当阻塞队列满了且线程数量大于最大线程数就会采用拒绝处理策略,四种策略为:

  1. ThreadPoolExecutor.AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常
  2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

3. 如何设置线程池参数(仅供参考)

1.根据服务器的类型来设置

  • IO阻塞型:线程数可以设置大一点,线程数 = CPU核数 * 1 / (1 - 阻塞系数)
  • CPU密集型:线程数 = CPU核数(一般情况下,也可以根据系统情况做相应的调整)

阻塞系数:
IO密集型:阻塞系数趋向于1,如果为0.9,则线程数 = CPU核数 * 10;如果为0.99,则线程数 = CPU核数 *100
CPU密集型:阻塞系数趋向于0,线程数 = CPU核数

2.无法判断服务器类型(通常为混合型)

  • 通过模拟生产环境来进行压测,不断调节线程参数和并发数,从而获得可靠数据(推荐)

常用的线程池

1. newCachedThreadPool()

线程池原理及常用线程池介绍_第4张图片

核心线程数为0,最大线程数几乎无限,阻塞队列使用的是 SynchronousQueue:同步移交队列

注意事项:使用时要注意控制线程的数量,防止因线程数太多导致OOM。

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(() -> System.out.println(index);
});
  		}
    }
}

2 . newFixedThreadPool(int nThreads)

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

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

线程池原理及常用线程池介绍_第5张图片

线程数量固定,传入的即是核心线程数,也是最大值;阻塞队列用的是无界的。

注意:使用时要注意控制无界队列的大小,防止因队列中等待的线程数太多导致OOM。

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(() -> {
     
     	try {
     
      		System.out.println(index);
      		Thread.sleep(2000);
    	 } catch (InterruptedException e) {
     
    	  	e.printStackTrace();
     }
    }
   });
  }
 }
}

3. newSingleThreadExecutor()

线程池原理及常用线程池介绍_第6张图片
核心线程数和最大线程数都是1,阻塞队列是无限的(单一线程,一次执行一个线程)

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(() -> {
     
     try {
     
      System.out.println(index);
      Thread.sleep(2000);
     } catch (InterruptedException e) {
     
      e.printStackTrace();
     }
    }
   });
  }
 }
}

你可能感兴趣的:(Java基础,线程池)