线程池简介
线程池:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
使用线程池的原因:
1.减少创建和销毁线程的次数,每个工作线程都可以重复的被利用,可执行多个任务。
2.由于线程池中线程数量可以调节,能够很方便的找到适应系统的最高效的工作方式。
在Java里面的线程池顶级接口是Executor,但是严格来说它并不是一个线程池,只是一个用来执行线程的工具,真正的线程池接口是ExecutorService.
下面介绍几个和线程池有关的类:
ExecutorService:
真正的线程池接口。
ScheduledExecutorService: 能和Timer以及TimerTask类似,具有定时重复执行任务的功能。
ThreaadPoolExecutor: ExecutorService的默认实现。
ScheduledThreadPoolExecutor: 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
由于线程池的性能和其中的最大线程数与系统的匹配相关联,所以对于一般人来说,原理了解的不是很清楚,配出来的线程池的性能也不高,在Executors类中提供了一些静态工厂,可以帮助我们生成一些常用的线程池。
1.newSingleThreadExecutor() : 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串执行所有任务。
2.newFixedThreadPool() : 创建固定大小的线程池。每次向线程池提交一个任务就创建一个县城,直到线程数量达到线程池的最大值,一旦达到了最大值就会保持不变,如果线程池中某个线程异常结束了,线程池会新增一个线程来补充。
3.newCachedThreadPool() : 创加一个可缓存的线程池,如果当前线程池中线程数量小于最大值,线程池会自动的回收部分空闲(60秒不执行任务)的线程,当任务数量变多,线程池再添加新的现车呢个来执行处理任务。这个线程池的大小没做限制,完全取决于该系统的承受能力。
4.newScheduledThreadPool() : 创建一个大小无线的线程池,这个线程池支持定时任务以及周期性执行任务。
4.newScheduledThreadPool() : 创建一个大小无线的线程池,这个线程池支持定时任务以及周期性执行任务。
上面介绍了这些东西,下面就来挨个试一试:
SingleThreadExecutor
public class ThreadPoolTest {
public static void main(String[] args) {
//创建一个单线程的线程池
ExecutorService pool = Executors.newSingleThreadExecutor();
//创建实现了Runnable接口的对象
Thread t1 = new TestThread();
Thread t2 = new TestThread();
Thread t3 = new TestThread();
Thread t4 = new TestThread();
Thread t5 = new TestThread();
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//关闭线程池
pool.shutdown();
}
}
class TestThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在执行...");
}
}
上面的代码创建了一个单线程的线程池,只有一个线程工作,所以往线程池中加入5个任务,也只是用一个线程在跑,一个任务执行的时候其他任务在队列中处于等待状态。
FixedThreadPool
public class ThreadPoolTest {
public static void main(String[] args) {
//创建一个固定大小为2的线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建实现了Runnable接口的对象
Thread t1 = new TestThread();
Thread t2 = new TestThread();
Thread t3 = new TestThread();
Thread t4 = new TestThread();
Thread t5 = new TestThread();
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//关闭线程池
pool.shutdown();
}
}
class TestThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在执行...");
}
}
线程池中有两个线程可以执行任务,往里面加入五个可执行任务,线程池会自动的将自动分配给两个线程去运行,我们能看到结果可能会不同,有时候线程1运行的任务要多一些,有时候会差不多。
CachedThreadPool
public class ThreadPoolTest {
public static void main(String[] args) {
//创建一个可缓冲的线程池,能自动的根据需求调整线程池中的线程数量
ExecutorService pool = Executors.newCachedThreadPool();
//创建实现了Runnable接口的对象
Thread t1 = new TestThread();
Thread t2 = new TestThread();
Thread t3 = new TestThread();
Thread t4 = new TestThread();
Thread t5 = new TestThread();
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//关闭线程池
pool.shutdown();
}
}
class TestThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在执行...");
}
}
我们基本上是在同时加入了5个任务,所以线程池快速的反应并创建了5个线程来执行任务,时间差很微小,可能由于打印的时间不一样,打印的顺序可能会有所不同,而这些任务执行时间都很短,如果在第一个任务执行完了,第五个任务刚加入的时候,线程池就不会再创建第五个线程了,而是用空闲出来的第一个线程来执行第5个任务,会出现如下图的情况:
4.ScheduleThreadpool
public class ThreadPoolTest {
<span style="white-space:pre"> </span>public static void main(String[] args) {
<span style="white-space:pre"> </span>//创建一个能执行定时任务的线程池
<span style="white-space:pre"> </span>ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(2);
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>exec.scheduleAtFixedRate(new Runnable() {//每隔一段时间就触发异常
@Override
public void run() {
//throw new RuntimeException();
System.out.println(Thread.currentThread().getName() + "================");
}
}, 1000, 5000, TimeUnit.MILLISECONDS);
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>exec.scheduleAtFixedRate(new Runnable() {//每隔一段时间打印系统时间,证明两者是互不影响的
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + System.nanoTime());
}
}, 1000, 2000, TimeUnit.MILLISECONDS);
<span style="white-space:pre"> </span>}
}
在这个只有一个线程的可定时执行任务的线程池中,存放着两个定时任务,但从结果可以看到,打印时间的任务还是每隔2秒执行一次,没有受到另外一个定时任务的影响。