java多线程:4、有关线程池的概念、四种创建方式、状态等问题

1、什么是线程池?

java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池。真正的线程池实现类是ExecutorService。

线程池是一种处理多线程的形式。处理过程中将任务添加队列,然后在创建线程之后启动这些任务,每个线程都有默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。

如果某个线程在托管代码中空闲,则线程池将插入另一个辅助线程来使所有处理器保持繁忙。

如果所有线程池都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后辅助线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才能启动。

线程池的作用优点:线程池可以提高程序的性能,加强对线程的统一管理。
(1)重用线程池的线程,避免因为线程的创建和销毁锁带来的性能开销

(2)有效控制线程池的最大并发数,避免大量的线程之间因抢占系统资源而阻塞

(3)能够对线程进行简单的管理,并提供一下特定的操作如:可以提供定时、定期、单线程、并发数控制等功能

2、创建线程池有哪几种方式?

(1)newCachedThreadPool——可缓存的线程池程

一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。但是线程池中的空闲线程都有超时限制,这个超时时长是60秒,超过60秒闲置线程就会被回收。

比较适合执行大量的耗时较少的任务,当整个线程池都处于闲置状态时,线程池中的线程都会超时被停止。调用execute将重用以前构造的线程(如果线程可用),所以可以实现线程重用。

public class MynewCachedThreadPool {

	public static void main(String[] args) {
		int num = 9; // 线程数
		// CountDownLatch是一个同步辅助类也可以使用AtomicInteger替代
		CountDownLatch doneSignal = new CountDownLatch(num);
		ExecutorService pool = Executors.newCachedThreadPool();
		for (int i = 0; i < num; i++)
			// 在未来某个时间执行给定的命令
			// 这里的CountDownLatch的构造函数中使用的int型变量的意思是需要等待多少个操作 的完成。
			pool.execute(new WorkerRunnable(doneSignal, i));
		try {
			doneSignal.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 子线程执行完毕,可以开始后续任务处理了
		System.out.println("所有任务执行完毕");

	}
}

class WorkerRunnable implements Runnable {
	private final CountDownLatch doneSignal;// 同步辅助类
	private final int i;// 计数

	WorkerRunnable(CountDownLatch doneSignal, int i) {
		this.doneSignal = doneSignal;
		this.i = i;
	}

	public void run() {
		// 子线程的任务
		try {
			doWork(i);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 任务执行完毕递减锁存器的计数
		doneSignal.countDown();
	}

	void doWork(int i) {
		System.out.println("这是第" + (i + 1) + "个任务");
	}
}

(2)newFixedThreadPool ——指定工作线程数量的线程池

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

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

(3)newScheduledThreadPool ——定长线程池

创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。
可以分析一下对比Timer类。

(4)newSingleThreadExecutor ——单线程化的线程池

创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行。
如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

3、线程池有哪几种状态?

线程池中有5种状态:Running(运行)、ShutDown(关机)、Stop(停止)、Tidying(整理)、Terminated(终止)。
在这里插入图片描述
(1)Running
能够接收新任务,以及对已添加的任务进行处理。

(2)ShutDown
不接收新任务,但能处理已添加的任务。
调用线程池的shutdown()接口时,线程池状态由running转为shutdown。

(3)Stop
不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
调用线程池的shutdownNow()接口时,线程池由running或者shutdown转为stop。

(4)Tidying
当所有的任务已终止,ctl记录的任务数量为0,线程池会变为TIDYING状态。当线程池变为Tidying状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为Tidying时,进行相应的处理;可以通过重载terminated()函数来实现。
当线程池在shutdown状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 shutdown 转为Tidying。
当线程池在Stop状态下,线程池中执行的任务为空时,就会由Stop转为Tidying。

(5)Terminated
线程池彻底终止。
线程池处在Tidying状态时,执行完terminated()之后,就会由 Tidying状态转为Terminated。

4、线程池中submit()和excute()方法有什么区别?

(1)所属接口不同。
submit(Callable task)、submit(Runnable task, T result)、submit(Runnable task)归属于ExecutorService接口。

execute(Runnable command)归属于Executor接口。ExecutorService继承了Executor。

(2)是否有返回值。
submit()有返回值。
execute没有返回值。

(3)异常捕获。
submit()方便做异常处理。通过Future.get()可捕获异常。

发布了57 篇原创文章 · 获赞 13 · 访问量 1096

猜你喜欢

转载自blog.csdn.net/weixin_42924812/article/details/105209914