一、线程池简介
线程的使用在Java中占有重要的地位,尤其是在执行耗时操作(Java网络编程)和异步访问(SWT中更新界面元素)时显得格外重要。线程虽然方便,但当需要同时执行大量的线程操作和特定的异步访问时,通过为每一个新任务单独新开线程就会造成大量的资源浪费,而且对同一类任务的多线程不能做到统一的管理,所以就有了多线程的技术。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
二、Java线程池
线程池的作用:
1、限制系统中执行线程的数量
线程池能够基于当前系统运行环境,通过自动或者手动的方式调整系统中运行线程的数量,从而达到节约系统资源的目的。设置线程池的数量可以防止程序无限制的开启线程而造成的资源浪费,任务少的时候放入线程池中执行,任务超过线程池数量限制时,排队等待。一个任务执行完毕后会从排队任务重取出任务放入线程池中执行,也就是说当一个新任务到来时,如果线程池未满则执行新任务,如果线程池已满就需进入等待队列直到有空闲的线程池资源。
2、对线程的统一管理
开发过程中会对多个相同或者相似的任务统一处理的情况,线程池可以对线程统一管理,比如:唤醒线程池中所有的线程,销毁所有的线程。
Java内置的线程池:
Java线程池的顶级接口为Executor,但一般使用较多的接口为ExecutorService,ExecutorService类中含有更多的对线程池的操作。
方法 | 描述 |
---|---|
isShutDown() | 如果此执行程序已关闭,则返回 true。 |
isTerminated() | 如果关闭后所有任务都已完成,则返回 true。 |
shutdown() | 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 |
shutdownNow() | 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 |
submit(task) | 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 |
ExecutorService类中常用的方法:
方法 | 描述 |
---|---|
isShutDown() | 如果此执行程序已关闭,则返回 true。 |
isTerminated() | 如果关闭后所有任务都已完成,则返回 true。 |
shutdown() | 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 |
shutdownNow() | 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 |
submit(task) | 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 |
为方便创建线程池Executors类中提供一些静态方法,用于快速创建线程池。
-
newCachedThreadPool
创建一个可缓存线程池,如果线程池大小超过处理需要,可灵活回收空闲线程,若无可回收线程,则创建线程。此线程池不会对线程池大小限制(但可以指定初始线程池的大小),线程池大小完全依赖于JVM空间的限制。 - **newFixedThreadPool **
创建一个定长线程池,可控制线程最大并发数,线程池一旦达到最大限制,超出的线程会在队列中等待。 - **newScheduledThreadPool **
创建一个定长线程池,它可安排在给定延迟后运行命令或者定期地执行。 - **newSingleThreadExecutor **
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
newCachedThreadPool测试代码:
public class NewCachedThreadPoolTest {
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(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
}
当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
newFixedThreadPool 测试代码:
public class FixedThreadPoolTest {
public static void main(String[] args) {
//创建一个可重用固定线程数的线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//关闭线程池
pool.shutdown();
}
}
输出结果:
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
newScheduledThreadPool 测试代码:
public class ScheduledThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
exec.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("================");
}
}, 1000, 5000, TimeUnit.MILLISECONDS);
exec.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(System.nanoTime());
}
}, 1000, 2000, TimeUnit.MILLISECONDS);
}
}
输出结果:
================
41070644908191
41072644693809
41074645495462
================
41076644808412
41078644896218
================
41080645104328
41082645027356
41084645111170
================
41086645135117
41088645164196
newScheduledThreadPool 测试代码:
public class SingleThreadExecutorTest
{
public static void main(String[] args) {
ExecutorService pool = Executors.newSingleThreadExecutor();
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
// 关闭线程池
pool.shutdown();
}
}
输出结果:
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
每种线程池都会有特定的使用场景,上述四种线程池能满足大部分的需求。