系统启动一个新线程的成本是比较高的,由于它涉及到与操做系统的交互。在这种状况下,使用线程池能够很好的提供性能,尤为是当程序中须要建立大量生存期很短暂的线程时,更应该考虑使用线程池。html
与数据库链接池相似的是,线程池在系统启动时即建立大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run方法。
java
除此以外,使用线程池能够有效地控制系统中并发线程的数量,但系统中包含大量并发线程时,会致使系统性能剧烈降低,甚至致使JVM崩溃。而线程池的最大线程数参数能够控制系统中并发的线程不超过此数目。数据库
在JDK1.5以前,开发者必须手动的实现本身的线程池,从JDK1.5以后,Java内建支持线程池。缓存
与多线程并发的全部支持的类都在java.lang.concurrent包中。咱们可使用里面的类更加的控制多线程的执行。多线程
1、Executors类并发
JDK1.5中提供Executors工厂类来产生链接池,该工厂类中包含以下的几个静态工程方法来建立链接池:app
一、public static ExecutorService newFixedThreadPool(int nThreads):建立一个可重用的、具备固定线程数的线程池。ide
二、public static ExecutorService newSingleThreadExecutor():建立一个只有单线程的线程池,它至关于newFixedThreadPool方法是传入的参数为1性能
三、public static ExecutorService newCachedThreadPool():建立一个具备缓存功能的线程池,系统根据须要建立线程,这些线程将会被缓存在线程池中。spa
四、public static ScheduledExecutorService newSingleThreadScheduledExecutor:建立只有一条线程的线程池,他能够在指定延迟后执行线程任务
五、public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize):建立具备指定线程数的线程池,它能够再指定延迟后执行线程任务,corePoolSize指池中所保存的线程数,即便线程是空闲的也被保存在线程池内。
上面的几个方法都有一个重载的方法,多传入一个ThreadFactory参数的重载方法,使用的比较少。
2、ExecutorService类
能够看到上面的5个方法中,前面3个方法的返回值都是一个ExecutorService对象。该ExecutorService对象就表明着一个尽快执行线程的线程池(只要线程池中有空闲线程当即执行线程任务),程序只要将一个Runnable对象或Callable对象提交给该线程池便可,该线程就会尽快的执行该任务。
ExecutorService有几个重要的方法:
方法摘要 | ||
---|---|---|
boolean |
isShutdown() 若是此执行程序已关闭,则返回 true。 |
|
boolean |
isTerminated() 若是关闭后全部任务都已完成,则返回 true。 |
|
void |
shutdown() 启动一次顺序关闭,执行之前提交的任务,但不接受新任务。 |
|
List<Runnable> |
shutdownNow() 试图中止全部正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 |
|
|
submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。 |
|
Future<?> |
submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 |
|
|
submit(Runnable task, T result) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 |
更详细的参考JDK API文档。
submit方法是对 Executor接口execute方法的更好的封装,建议使用submit方法。
3、ScheduleExecutorService类
在上面的5个方法中,后面2个方法的返回值都是一个ScheduleExecutorService对象。ScheduleExecutorService表明可在指定延迟或周期性执行线程任务的线程池。
ScheduleExecutorService类是ExecutorService类的子类。因此,它里面也有直接提交任务的submit方法,而且新增了一些延迟任务处理的方法:
方法摘要 | ||
---|---|---|
|
schedule(Callable<V> callable, long delay, TimeUnit unit) 建立并执行在给定延迟后启用的 ScheduledFuture。 |
|
ScheduledFuture<?> |
schedule(Runnable command, long delay, TimeUnit unit) 建立并执行在给定延迟后启用的一次性操做。 |
|
ScheduledFuture<?> |
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 建立并执行一个在给定初始延迟后首次启用的按期操做,后续操做具备给定的周期;也就是将在 initialDelay后开始执行,而后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。 |
|
ScheduledFuture<?> |
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) 建立并执行一个在给定初始延迟后首次启用的按期操做,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。 |
下面看看线程池的简单使用:
一、固定大小的线程池:
[java] view plaincopy
package com.tao.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
ExecutorService pool=Executors.newFixedThreadPool(5);//建立一个固定大小为5的线程池
for(int i=0;i<7;i++){
pool.submit(new MyThread());
}
pool.shutdown();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行。。。");
}
}
输出结果:
[java] view plaincopy
pool-1-thread-1正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-5正在执行。。。
pool-1-thread-1正在执行。。。
能够看到虽然咱们呢建立了7个MyThread线程对象,可是因为受线程池的大小限制,只是开启了5个线程,这样就减小了并发线程的数量。
二、单任务线程池:
[java] view plaincopy
public class PoolTest {
public static void main(String[] args) {
ExecutorService pool=Executors.newSingleThreadExecutor();//建立一个单线程池
for(int i=0;i<7;i++){
pool.submit(new MyThread());
}
pool.shutdown();
}
}
输出结果:
[java] view plaincopy
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
能够看到,线程池只开启了一个线程。
三、建立可变尺寸的线程池
[java] view plaincopy
public class PoolTest {
public static void main(String[] args) {
ExecutorService pool=Executors.newCachedThreadPool();
for(int i=0;i<5;i++){
pool.submit(new MyThread());
}
pool.shutdown();
}
}
看输出结果:
[java] view plaincopy
pool-1-thread-1正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-5正在执行。。。
能够看到,咱们没有限制线程池的大小,可是它会根据需求而建立线程。
四、延迟线程池
[java] view plaincopy
public class PoolTest {
public static void main(String[] args) {
ScheduledExecutorService pool=Executors.newScheduledThreadPool(6);
for(int i=0;i<4;i++){
pool.submit(new MyThread());
}
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);
pool.shutdown();
}
}
输出结果:
[java] view plaincopy
pool-1-thread-1正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-6正在执行。。。
pool-1-thread-1正在执行。。。
能够明显看到,最后两个线程不是当即执行,而是延迟了1秒在执行的。
五、单任务延迟线程池
[java] view plaincopy
public class PoolTest {
public static void main(String[] args) {
ScheduledExecutorService pool=Executors.newSingleThreadScheduledExecutor();
for(int i=0;i<4;i++){
pool.submit(new MyThread());
}
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);
pool.shutdown();
}
}
上面咱们使用的是JDK帮我封装好的线程池,咱们也能够本身定义线程池,查看源码,咱们发现,Excutors里面的得到线程的静态方法,内部都是调用ThreadPoolExecutor的构造方法。好比:
[java] view plaincopy
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
能够看到,它是经过调用ThreadPoolExecutor的构造方法来返回一个线程池的。因此,咱们也能够本身手动的调用ThreadPoolExecutor的各类构造方法,来定义本身的线程池规则,不过通常状况下,使用自带的线程池就够了,不须要本身来实现。