线程池的基本创建方式and执行流程!

 一、什么是线程池?

1.线程池

线程池是一种线程管理的机制,它是一组线程的集合,可以用来执行多个任务。线程池维护了一个固定数量的线程集合,可以从线程池中取出一个线程来执行任务,当任务执行完毕后,线程又会返回到线程池中,等待下一个任务的到来。当所有线程都处于忙碌状态时,线程池会创建一个新的线程进行处理,如果超过线程最大数,则进入等待队列,进行等待。

2.线程池的优点 

  • 资源控制:线程池可以限制线程数量,避免线程过多导致系统资源不足。

  • 提高响应速度:线程池中的线程已经预先创建,可以避免线程创建时的开销,从而可以更快地响应请求。

  • 提高系统稳定性:线程池可以避免系统过载,从而提高系统的稳定性和可靠性。

  • 提高资源利用率:线程池中的线程可以重复利用,避免了线程的频繁创建和销毁,从而提高了资源的利用率。

二、线程池的创建方式

  • Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
public class Test01 {
	public static void main(String[] args) throws InterruptedException {
		//创建固定数目的线程池
		ExecutorService executorService=Executors.newFixedThreadPool(4);
		
		//提交六个线程任务
		for(int i=0;i<6;i++) {
			executorService.execute(new Task("线程"+i));
		}
		executorService.shutdown();
		
		while(!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
			System.out.println("线程池暂未关闭!");
		}
		System.out.println("线程池已经关闭!");
	}

}
  • Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
public class Test02 {
	public static void main(String[] args) throws InterruptedException {
	
	ExecutorService executorService=Executors.newCachedThreadPool();
	
	//提交六个线程任务
	for(int i=0;i<60;i++) {
		executorService.execute(new Task("线程"+i));
	}
	executorService.shutdown();
	
	while(!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
		System.out.println("线程池暂未关闭!");
	}
	System.out.println("线程池已经关闭!");
}

}
  • Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
  • Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
//周期性执行线程任务
public class Test04 {
	public static void main(String[] args) {
		ScheduledExecutorService executorService=Executors.newScheduledThreadPool(3);
		
		//延迟3秒钟执行任务执行一次
//		System.out.println("当前时间:"+LocalDateTime.now());
//		executorService.schedule(new Task("任务A"), 3, TimeUnit.SECONDS);
		
		//延迟一秒钟后执行任务,每隔3秒钟执行一次
//		System.out.println("当前时间:"+LocalDateTime.now());
//		executorService.scheduleAtFixedRate(new Task("任务A"), 1, 3, TimeUnit.SECONDS);
		
		System.out.println("当前时间:"+LocalDateTime.now());
		executorService.scheduleWithFixedDelay(new Task("任务A"), 1, 3, TimeUnit.SECONDS);
		
	}

}
  • ThreadPoolExecutor:最原始的创建线程池的方式,需要设置参数。
  1. 第一个参数corePoolSize:是指核心线程数,线程池中始终存活的线程数。
  2. 第二个参数maximumPoolSize:最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。
  3. 第三个参数keepAliveTime:非核心线程空闲后可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。
  4. 等四个参数为单位:是和参数 三存活时间配合使用的,合在一起用于设定线程的存活时间 ,参数 keepAliveTime 的时间单位有以下 7 种可选。
  5. 第五个参数workQueue:一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全。
  6. 第六个参数threadFactory:线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。

常见的阻塞队列 

  1. ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
  2. LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
  3. SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
  4. PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  5. DelayedWorkQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
public class Test05 {
	public static void main(String[] args) {
		ExecutorService executorService=new ThreadPoolExecutor(10, 11, 1, TimeUnit.SECONDS, 
				new LinkedBlockingDeque(),
				new MyThreadFactory());	
		
		//提交六个线程任务
		for(int i=0;i<60;i++) {
			executorService.execute(new Task("线程"+i));
		}
				
	}

}
public class MyThreadFactory implements ThreadFactory {
	
	private final AtomicInteger number=new AtomicInteger();

	@Override
	public Thread newThread(Runnable r) {
		Thread t=new Thread(r);
		t.setName("订单线程"+number.incrementAndGet());
		return t;
	}

}
class Task implements Runnable{
	private String TaskName;
	
	public Task(String taskName) {
		this.TaskName = taskName;
	}

	@Override
	public void run() {
		System.out.println("启动线程===>"+this.TaskName);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("结束线程===>"+this.TaskName);
		
	}	
}

三、线程的常用接口/类和方法

1.接口/类: 

ExecutorService接口:进行线程池的操作访问。

Executors类:创建线程池的工具类。

ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制。

 2.执行线程任务

awaitTermination(long timeout,TimeUnit unit) :每间隔一定时间检查一次线程执行任务状态,等待线程池关闭。

public class Test01 {
	public static void main(String[] args) {
		//创建1-100w之间的多有数字累加和,每10W个数字交给一个线程处理
		//创建一个固定大小的线程池
		ExecutorService executorService=Executors.newFixedThreadPool(4);
		
		//创建集合用于保存Future执行结果
		List> futureList=new ArrayList>();
		
		//每10w个数字封装成一个callable线性任务,并提交给线程池
		for(int i=0;i<900000;i+=100000) {
			Future total=executorService.submit(new CalcTask(i, i+100000));
			futureList.add(total);		
		}
		
		try {
			int total=0;
			for(int i=0;i{
	private int begin,end;
	
	public CalcTask(int begin,int end) {
		this.begin=begin;
		this.end=end;
	}

	@Override
	public Integer call() throws Exception {
		int total=0;
		for(int i=begin;i<=end;i++) {
			total+=i;
		}
		System.out.printf("线程%s计算%d-%d范围的任务结束!\n",Thread.currentThread().getName(),begin,end);
		return total;
	}
	
}

线程的关闭

 shutdown():关闭线程池,但会等正在执行任务的线程结束再关闭。

shutdownNow():立即关闭。

四、线程池的执行流程 

线程池中的线程数量是有限的,每个线程可以处理多个任务。线程池的工作原理主要分为以下几个步骤:

  1. 创建一个线程池:初始化线程池,为线程池分配一定数量的线程,设置线程池中线程的最大数量、最小数量、空闲线程的存活时间等属性。
  2. 将任务加入线程池:当有任务需要处理时,线程池会从任务队列中取出一个任务,将其分配给一个空闲线程进行处理,如果没有空闲线程那么线程池会对判断当前存活的线程数是否小于核心线程数。如果小于,则创建一个新的线程去处理新任务;如果大于核心线程数则回去检查工作等待队列,如果工作队列未满则会放入工作队列中进行等到有空闲线程去处理它,如果工作队列已经满了,则需要判断当前存活的线程数是否达到最大线程数,如果没有就创建一个新的非核心线程进行处理新任务,如果达到了,则采用拒绝策略处理新任务。
  3. 线程处理任务:线程取出任务后,进行任务处理,处理完毕后继续从任务队列中取出任务进行处理。
  4. 任务队列:线程池中的任务队列用于存放等待执行的任务,线程池中的线程会从任务队列中取出任务进行处理。
  5. 线程池关闭:当不需要处理任务时,可以关闭线程池,线程池会将所有未执行的任务清空,并释放所有的线程资源。

线程池的基本创建方式and执行流程!_第1张图片

拒绝策略 

常见的线程池拒绝策略有:

  1. CallerRunsPolicy:由调用线程处理该任务,即在提交任务的线程中执行该任务,谁调用谁处理。

  2. AbortPolicy:直接抛出异常,阻止系统正常工作。

  3. DiscardPolicy:直接丢弃任务,不进行处理。

  4. DiscardOldestPolicy:丢弃队列中最旧的任务,尝试为当前任务腾出位置。

 五、线程池的状态

线程池的状态分为:running, shutdown,stop,tidying,terminated。

running

  • 状态说明:线程池处在运行状态时,能够接收新任务,并处理工作队列中的任务。 
  • 状态切换:线程池的初始化状态是running。换句话说,线程池被一旦被创建,就处于running状态,并且线程池中的任务数为0。

shutdown

  • 状态说明:线程池处在关闭状态时,不接收新任务,但能处理已添加的任务。 
  • 状态切换:调用线程池的shutdown()接口时,线程池由running-->shutdown。

stop

  • 状态说明:线程池处在停止状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 
  • 状态切换:调用线程池的shutdownNow()接口时,线程池由running -->stop。

tidying

  • 状态说明:整理状态。该状态表明所有的任务已经运行终止,记录的任务数量为0;线程池中执行的任务为空,进入tidying状态。
  • 状态切换:当线程池在shutdown状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 shutdown-->tidying。 当线程池在stop状态下,线程池中执行的任务为空时,就会由stop-->tidying。

terminated

  •  状态说明:线程池彻底终止,就变成terminated状态。 
  • 状态切换:线程池处在tidying状态时,执行完terminated()之后,就会由tidying-->terminated。

你可能感兴趣的:(java,开发语言)