Java线程池

线程池

优点:

  1. 线程的创建和销毁是需要耗费资源的,使用线程池可以有效的减少创建和销毁线程的次数,每个工作线程都可以重复使用。
  2. 可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。

工作流程:

  • 当提交一个任务时,线程池会创建一个新的线程执行任务,直到当前线程池中的线程数等于corePoolSize:线程池的大小。

  • 如果当前线程数量已经到达了corePoolSize,继续提交的任务被保存到阻塞队列中,等待获取执行完成的任务释放线程,从而执行等待任务。

  • 如果阻塞队列满了,那就再创建新的线程去执行任务,直到线程池中的线程数达到maximumPoolSize,这时候如果再有任务来,只能执行拒绝方案。

ThreadPoolExecutor类

java.util.concurrent.ThreadPoolExecutor类 是Java描述线程池中最核心的一个类。

  • 核心参数:

    • corePoolSize: 核心池大小。

    • maxmumPoolSize: 线程池最大线程数,线程池中线程数量的上限。

    • ThreadPoolExecutor提供了动态修改线程池容量的方法:

      • setCorePoolSize() 修改 corePoolSize

      • setMaxmumPoolSize() 修改 maxmumPoolSize

    • keepAliveTime:空闲的线程可以存活的时间。

      线程池中的线程数大于corePoolSize,线程池进入了紧急预案状态,当线程数小于corePoolSize,keepAliveTime不起作用。

      corePoolSize:5,线程池中有6个线程,如果某个线程的闲置时间达到了10s,则释放该线程。
      直到线程池中的线程数量小于等于corePoolSize,keepAliveTime失效。

    • unit:参数keepAliveTime的时间单位,TimeUnit中有7个常量来表示7种不同的时间单位。

          TimeUnit.DAYS			 天
          
          TimeUnit.HOURS			小时
          
          TimeUnit.MINUTES		分钟
          
          TimeUnit.SECONDS		 秒
          
          TimeUnit.MILLSECONDS	毫秒
          
          TimeUnit.MICROSECONDS   微秒
          
          TimeUnit.NANOSECONDS    纳秒
      
      
    • workQueue:一个阻塞队列,用来存储等待执行的任务,一般阻塞队列有以下几种选择:

      • ArrayBlockingQueue: 基于数组的先进先出队列,创建时必须制定大小。
      • LinkedBlockingQueue: 基于链表的先进先出队列,如果创建时没有指定大小,默认Integer.MAX_VALUE。
      • SynchronousQueue: 不会保存提交的任务,而是直接创建一个新的线程来执行新的任务。
      • PriorityBlockingQueue: 具有优先级的阻塞队列。
  • threadFactory: 线程工厂,用来创建线程。

  • handler: 表示拒绝处理任务所采取的策略,有以下四种取值。

    • ThreadPoolExecutor.AbortPolicy: 丢弃任务并且抛出异常。
    • ThreadPoolExecutor.DiscardPolicy: 丢弃任务但是不抛出异常。
    • ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列最前面的任务,然后重新执行当前任务。
    • ThreadPoolExecutor.CallerRunsPolicy: 由调用线程处理该任务。
线程池的状态
  1. RUNNING: 该状态下的线程池可以接收新的任务,并且处理阻塞队列中的任务。

  2. SHUTDOWN: 该状态下的线程池不接收新的任务,但是会处理阻塞队列中的任务。

  3. STOP: 该状态下的线程池不接收新的任务,也不会处理阻塞队列中的任务,并且会中断正在运行的任务。

  4. TIDYING: 该状态表示线程池对线程进行整理优化。

  5. TERMINATED: 该状态表示线程池停止工作。

  • ThreadPoolExecutor 继承了 AbstractExecutorService。

  • AbstractExecutorService是一个抽象类,实现了ExecutorService接口。

  • ExecutorService继承了Executor:

    • Executor是一个顶层接口,该接口中只声明了一个方法execute(Runnable),返回值为void,参数是Runnable类型的,就是用来执行传入的任务。
    • ExecutorService接口继承了Executor接口,并声明了一些方法:submit,invokeAll,shutDown。
    • 抽象类AbstractExecutorService实现了ExecutorService接口,基本上实现了ExecutorService中声明的所有方法。
    • ThreadPoolExecutor继承了AbstractExecutorService,ThreadPoolExecutor实现了全部的方法。
ThreadPoolExecutor类中常用的方法:
  • execute()
    • 是一个核心方法,通过该方法可以向线程池提交一个任务,由线程池去安排执行。
  • submit()
    • ExecutorService接口中声明的方法,也是用来向线程池提交任务的,和execute()方法不同,可以返回执行任务的结果。
  • shutdown()
    • 关闭线程池,不会立即终止线程池,而是等待阻塞队列中的任务全部执行完毕之后在终止,同时也不会再接收新的任务。
  • shutdownNow()
    • 立即终止线程池,并中断正在执行的任务,并且清空阻塞队列,返回尚未执行的任务。

execute内部实现:

  • 通过workCountof()方法获取当前线程池中的线程数。如果小于corePoolSize,就通过addWorker()创建线程并执行该任务,否则,将该任务放入阻塞队列。

  • 如果能够成功的放入到阻塞队列中,若当前线程池是非RUNNING状态,则将该任务从阻塞队列中移除,然后执行reject()拒绝处理该任务。如果当前线程池处于RUNNING状态,则需要再次检查线程池,如果有空闲的线程则执行该任务。

  • 如果不能将任务放入阻塞队列中,说明阻塞队列已满,就通过addWorker()方法尝试创建新的线程来执行该任务。如果addWorker()方法失败,则表示当前线程池中的线程数已经达到了maxmumPoolSize,执行reject()拒绝处理该任务。

submit内部实现:

会将提交的任务封装成一个FutureTask对象,FutureTask类实现了Runnable接口,这样就可以通过Executor.execute()提交FutureTask到线程池中等待被执行,最终执行的是FutureTask的run方法。

总结

当提交一个任务时,线程池会创建一个新的线程去执行该任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务会被存入阻塞队列,等待被执行;如果阻塞队列满了,那就创建一个新的线程来执行当前任务,直到线程池中的线程数等于maxmumPoolSize,这时如果再有任务提交过来,只能执行reject()方法来拒绝处理。

package com.southwind.thread;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Test {
	public static void main(String[] args) {
		ThreadPoolExecutor executor = new ThreadPoolExecutor(
				5, 10, 200, TimeUnit.MILLISECONDS, 
				new ArrayBlockingQueue<Runnable>(5));
				
		for (int i = 0; i < 10; i++) {
			MyTask task = new MyTask(i);
			executor.execute(task);
			System.out.println("线程池中的线程数:"+executor.getCorePoolSize()+",队列中等待执行的任务数:"+executor.getQueue().size()+",已执行完成的任务数:"+executor.getCompletedTaskCount());
		}
		executor.shutdown();
	}
}

class MyTask implements Runnable{
	private int id;
	public MyTask(int id) {
		this.id = id;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("正在执行task"+id);
		try {
			Thread.currentThread().sleep(4000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("task"+id+"执行完毕");
	}
}

实际开发中,不提倡直接实例化ThreadPoolExecutor,而是通过Executors类提供的静态方法来创建线程池:

  1. 创建一个固定大小的线程池,任务超过10个时,将任务放入阻塞队列中。
package com.southwind.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test2 {
	public static void main(String[] args) {
		ExecutorService service =  Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++) {
			MyTask2 task = new MyTask2();
			service.execute(task);
		}
		service.shutdown();
		

//没有使用线程池,直接用Thread调用。
//		for (int i = 0; i < 10; i++) {
//			MyTask2 task = new MyTask2();
//			new Thread(task).start();
//		}
		
	}
}

class MyTask2 implements Runnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.currentThread().sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("...");
	}
}
  1. 创建一个缓存线程池

newCachedThreadPool(),线程数可以达到Integer.MAX_VALUE,2147483647,内部使用SynchronousQueue作为阻塞队列。

ExecutorService service =  Executors.newCachedThreadPool();
  1. 创建单例线程池。

newSingleThreadExecutor(),线程池中只有一个线程。

package day_4_16;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test3 {
	public static void main(String[] args) {
		ExecutorService sevice = Executors.newSingleThreadExecutor();
		for(int i = 0; i < 10; i++) {
			MyTask2 task = new MyTask2();
			sevice.execute(task);
		}
		sevice.shutdown();
	}
}

class MyTask2 implements Runnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.currentThread().sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("...");
	}
}

  1. 任务调度线程池。

newScheduledThreadPool()

package day_4_16;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test4 {
	public static void main(String[] args) {
		ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
		// 延迟5秒执行,每隔3秒执行一次任务
		service.scheduleAtFixedRate(new MyTask2(), 5, 3, TimeUnit.SECONDS);
//		service.shutdown();
	}
}

class MyTask2 implements Runnable {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.currentThread().sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("...");
	}
}


返回多线程目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值