1、介绍

不使用线程池面临的问题

众所周知,java中线程的创建、切换、销毁都是比较消耗计算机资源的,若是线程执行的任务逻辑比较简单,创建、销毁线程消耗的计算机资源可能大于任务本身。特别是频繁的创建大量的线程,大量的线程在上下文切换的同时,系统也可能变得不稳定。

线程池优点
  • 减少资源消耗:通过线程复用,避免重复创建、销毁线程造成额外的资源消耗。
  • 提高响应速度:任务到达时,已有线程直接执行任务,无需实时创建线程造成的时间消耗。
  • 资源上限管理:线程池的大小是预设的,避免系统无上限创建线程造成内存溢出。

2、7大核心参数

列表

参数

说明

int corePoolSize

核心线程数

int maximumPoolSize

最大线程数

long keepAliveTime

空闲线程存活时间

TimeUnit unit

空闲线程存活时间的单位

BlockingQueue workQueue

线程池任务队列

可详见

ThreadFactory threadFactory

创建线程的工厂

RejectedExecutionHandler handler

拒绝策略

详解
  • corePoolSize:线程池中一直存在的线程数量,即使线程为空闲状态也会一直存在,除非将属性(allowCoreThreadTimeOut)设置为true。
  • maximumPoolSize:线程池中允许存在的最大线程数量。
  • keepAliveTime:当线程数量大于核心线程数量时,多余且空闲的线程在被销毁前等待任务的最大时间。
  • unit:keepAliveTime参数的时间单位,如毫秒、秒、分钟、小时等等。
  • workQueue:线程池用来暂时存放任务的阻塞队列。
  • threadFactory:线程池创建线程时调用的工厂方法,通过此方法可以设置线程的优先级、线程命名规则以及线程类型(用户线程还是守护线程)等。
  • handler:当线程池的任务超出线程池队列可以存储的最大值之后,执行的策略。
图解流程

Java中释放当前线程资源 java线程池释放线程_Java中释放当前线程资源

API图示

Java中释放当前线程资源 java线程池释放线程_线程池_02

3、使用案例

public class Demo {
    public static void main(String[] args)throws Exception {
        /**
         * corePoolSize: 3
         * maximumPoolSize: 5
         * keepAliveTime: 100
         * unit: TimeUnit.MILLISECONDS
         * workQueue: new ArrayBlockingQueue<Runnable>(1)
         *
         * 说明:线程池核心线程数量3,最大线程5,非核心线程空闲存活100毫秒,阻塞队列是基于数组的大小为1的队列。
         * 线程工厂、拒绝策略使用ThreadPoolExecutor的默认值。
         */
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 100, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
        executor.execute(()->{
            Thread thread = Thread.currentThread();
            System.out.println("线程名称:"+thread.getName());
            System.out.println("线程优先级:"+thread.getPriority());
            System.out.println("线程池线程数量:"+executor.getPoolSize());
        });
    }
}

3、几种常见线程池

在上面的演示案例中,直接调用ThreadPoolExecutor类的构造函数创建线程池,在实际编码时,其实并不需要我们手动创建线程池。

JDK根据不同的使用场景,通过调节7大核心参数,Executors类提供了大量静态函数供我们调用创建线程池。

newCachedThreadPool(可缓存的线程池)
//使用方式
ExecutorService threadPool = Executors.newCachedThreadPool();

//源码
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
newFixedThreadPool(固定大小的线程池)
//使用方式
ExecutorService threadPool = Executors.newFixedThreadPool(5);

//源码
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
newSingleThreadPool(单个线程的线程池)
//使用方式
ExecutorService threadPool = Executors.newSingleThreadExecutor();

//源码
public static ExecutorService newSingleThreadExecutor() {
	return new FinalizableDelegatedExecutorService
		(new ThreadPoolExecutor(1, 1,
								0L, TimeUnit.MILLISECONDS,
								new LinkedBlockingQueue<Runnable>()));
}

4、线程复用原理

我们知道,一个线程对象是不能被启动多次的,如下演示:

//代码
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程名称:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        myThread.start();
    }
}

//运行结果
线程名称:Thread-0
Exception in thread "main" java.lang.IllegalThreadStateException

那要怎么实现线程复用,让一个线程对象运行多个任务(Runnable)呢,演示简易代码:

//代码
public class MyThread extends Thread {
    private BlockingQueue<Runnable> tasks = null;
    public MyThread(BlockingQueue<Runnable> tasks){
        this.tasks = tasks;
    }

    @Override
    public void run() {
        for(;;){
            try {
                Runnable task = tasks.take();
                task.run();
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    }

    public static void main(String[] args) {
        ArrayBlockingQueue<Runnable> runnables = new ArrayBlockingQueue<>(3);
        runnables.offer(()->System.out.println("线程名称:"+Thread.currentThread().getName()));
        runnables.offer(()->System.out.println("线程名称:"+Thread.currentThread().getName()));
        runnables.offer(()->System.out.println("线程名称:"+Thread.currentThread().getName()));
        MyThread myThread = new MyThread(runnables);

        myThread.start();
    }
}

//运行结果
线程名称:Thread-0
线程名称:Thread-0
线程名称:Thread-0

JDK8线程池实现线程复用关键源码

/**
 * 此类是线程池的任务类,内部封装了Thread属性,启动内部的Thread便会运行Worker的run方法。
 */
private final class Worker
	extends AbstractQueuedSynchronizer
	implements Runnable{
	/**
	 * This class will never be serialized, but we provide a
	 * serialVersionUID to suppress a javac warning.
	 */
	private static final long serialVersionUID = 6138294804551838833L;

	/** Thread this worker is running in.  Null if factory fails. */
	final Thread thread;
	/** Initial task to run.  Possibly null. */
	Runnable firstTask;
	/** Per-thread task counter */
	volatile long completedTasks;

	/**
	 * Creates with given first task and thread from ThreadFactory.
	 * @param firstTask the first task (null if none)
	 */
	Worker(Runnable firstTask) {
		setState(-1); // inhibit interrupts until runWorker
		this.firstTask = firstTask;
		//这里创建线程对象,将对象自身传递进去,this.thread一旦启动(调用start),就会运行Worker类的run方法。
		this.thread = getThreadFactory().newThread(this);
	}

	/** Delegates main run loop to outer runWorker  */
	//this.thread一旦启动(调用start),run方法就会自动运行
	public void run() {
		//循环从阻塞队列里面取出Runnable对象,然后执行其run方法。
		runWorker(this);
	}

	// Lock methods
	//
	// The value 0 represents the unlocked state.
	// The value 1 represents the locked state.

	protected boolean isHeldExclusively() {
		return getState() != 0;
	}

	protected boolean tryAcquire(int unused) {
		if (compareAndSetState(0, 1)) {
			setExclusiveOwnerThread(Thread.currentThread());
			return true;
		}
		return false;
	}

	protected boolean tryRelease(int unused) {
		setExclusiveOwnerThread(null);
		setState(0);
		return true;
	}

	public void lock()        { acquire(1); }
	public boolean tryLock()  { return tryAcquire(1); }
	public void unlock()      { release(1); }
	public boolean isLocked() { return isHeldExclusively(); }

	void interruptIfStarted() {
		Thread t;
		if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
			try {
				t.interrupt();
			} catch (SecurityException ignore) {
			}
		}
	}
}

/**
 * Worker类中run方法调用的方法,此方法循坏从阻塞队列里面取出Runnable实现类对象,
 * 然后执行Runnable对象的run方法,从而实现了线程的复用。
 */
final void runWorker(Worker w) {
	Thread wt = Thread.currentThread();
	Runnable task = w.firstTask;
	w.firstTask = null;
	w.unlock(); // allow interrupts
	boolean completedAbruptly = true;
	try {
		while (task != null || (task = getTask()) != null) {
			w.lock();
			// If pool is stopping, ensure thread is interrupted;
			// if not, ensure thread is not interrupted.  This
			// requires a recheck in second case to deal with
			// shutdownNow race while clearing interrupt
			if ((runStateAtLeast(ctl.get(), STOP) ||
				 (Thread.interrupted() &&
				  runStateAtLeast(ctl.get(), STOP))) &&
				!wt.isInterrupted())
				wt.interrupt();
			try {
				beforeExecute(wt, task);
				Throwable thrown = null;
				try {
					task.run();
				} catch (RuntimeException x) {
					thrown = x; throw x;
				} catch (Error x) {
					thrown = x; throw x;
				} catch (Throwable x) {
					thrown = x; throw new Error(x);
				} finally {
					afterExecute(task, thrown);
				}
			} finally {
				task = null;
				w.completedTasks++;
				w.unlock();
			}
		}
		completedAbruptly = false;
	} finally {
		processWorkerExit(w, completedAbruptly);
	}
}

5、线程超时销毁原理

JDK8实现非核心线程超时销毁关键源码

final void runWorker(Worker w) {
	Thread wt = Thread.currentThread();
	Runnable task = w.firstTask;
	w.firstTask = null;
	w.unlock(); // allow interrupts
	boolean completedAbruptly = true;
	try {
        /**
         * 此处getTask方法为从阻塞队列里面取出Runnable对象
         * 如果取出的对象为null,则停止while循环,执行processWorkerExit方法,销毁此Worker任务线程
         */
		while (task != null || (task = getTask()) != null) {
			w.lock();
			// If pool is stopping, ensure thread is interrupted;
			// if not, ensure thread is not interrupted.  This
			// requires a recheck in second case to deal with
			// shutdownNow race while clearing interrupt
			if ((runStateAtLeast(ctl.get(), STOP) ||
				 (Thread.interrupted() &&
				  runStateAtLeast(ctl.get(), STOP))) &&
				!wt.isInterrupted())
				wt.interrupt();
			try {
				beforeExecute(wt, task);
				Throwable thrown = null;
				try {
					task.run();
				} catch (RuntimeException x) {
					thrown = x; throw x;
				} catch (Error x) {
					thrown = x; throw x;
				} catch (Throwable x) {
					thrown = x; throw new Error(x);
				} finally {
					afterExecute(task, thrown);
				}
			} finally {
				task = null;
				w.completedTasks++;
				w.unlock();
			}
		}
		completedAbruptly = false;
	} finally {
        //销毁此Worker任务线程
		processWorkerExit(w, completedAbruptly);
	}
}

/**
 * 从阻塞队列里面取出Runnable对象
 */
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        /**
         * allowCoreThreadTimeOut若设置为true,则核心线程也会被超时销毁
         * wc > corePoolSize(判断当前线程数量是否大于核心线程数,若大于则当前线程需要做超时销毁)
         * 若timed==true:当前任务线程需要做超时销毁操作。
         */
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            /**
             * 当阻塞队列为空时
             * timed==true:workQueue.poll返回null。
             * timed==false:workQueue.take阻塞直到队列中有元素为止。
             */
            Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}