在开发服务器软件时,我们经常需要处理执行时间比较短但是数量巨大的请求,如果每个请求都创建一个新的线程来处理,那就会导致线程太多而遇上系统性能的瓶颈,因为线程的创建和销毁需要JVM进行处理,如果请求时间太短,那就在线程的创建和销毁对象上花费的时间大于线程执行的时间,如果是这样,那性能就大大降低了。
考虑到以上问题,java在jdk5中提供了线程池的支持,当然我们可以自己来实现线程池。jdk5中的线程池主要是用来提供高并发的访问处理,并且还可以复用池中空闲的线程对象。核心机制就是提供一个执行效率比较高的线程池,对线程池中的线程对象管理,包含创建和销毁,只需要将任务传递给线程池即可,线程对象的处理都在线程池中。
我们要创建线程池钱,先来了解一下Executor接口,因为大部分创建线程池的类都实现了此接口。可查看此接口的源码,只有一个方法如下:
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the Executor implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution.
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
Executor是接口,不能直接使用,还得需要它的实现类,下图就是比较完整的Executor接口相关的继承结构
ExecutorService是Executor子接口,所以也不能实例化,但其内部提供了比较多的方法,如下图:
ExecutorService唯一一个实现类AbstractExecutorService,从名字上看是抽象的,所以也不能实例化,我们再看一下AbstractExecutorService的子类ThreadPoolExecutor,这个类不是抽象的,可以实例化,所以使用这个类中的方法来实现创建多线程。其类结构如下:
以上就是对线程池的创建应用比较多的接口Executor和子接口ExecutorService的说明,再到可实例化的对象ThreadPoolExecutor的简单描述,下面我们看下具体创建线程池的类以及使用方法。
JDK5中提供了两种方式来创建线程池,Executors工厂类和ThreadPoolExecutor对象,下面我们看看这两个的创建细节
类中方法如下:
此方法创建的是无界的线程池,自己管理线程池。此线程池中存放的个数最大值是Integer.MAX_VALUE。
示例:
public class ExecutorsDemo {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
}
});
}
}
执行结果:
pool-1-thread-1 begin 1506671576457
pool-1-thread-2 begin 1506671576458
pool-1-thread-2 end 1506671578460
pool-1-thread-1 end 1506671578460
从结果中可以看到,创建了两个线程,而且执行是无序的。
此方法指明了可以创建线程的最大数,当然线程也可以复用。
示例1:
public class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " name = " + name + " begin with " + System.currentTimeMillis());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " name = " + name + " end with " + System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
}
}
}
调试程序:
public class Main {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
service.execute(new MyRunnable((i + 1) + ""));
}
for (int i = 0; i < 3; i++) {
service.execute(new MyRunnable((i + 1) + ""));
}
service.shutdown();
}
}
调试结果:
pool-1-thread-1 name = 1 begin with 1506691581334
pool-1-thread-2 name = 2 begin with 1506691581334
pool-1-thread-3 name = 3 begin with 1506691581334
pool-1-thread-1 name = 1 end with 1506691583336
pool-1-thread-2 name = 2 end with 1506691583336
pool-1-thread-3 name = 3 end with 1506691583336
pool-1-thread-2 name = 2 begin with 1506691583336
pool-1-thread-1 name = 1 begin with 1506691583336
pool-1-thread-3 name = 3 begin with 1506691583336
pool-1-thread-3 name = 3 end with 1506691585340
pool-1-thread-2 name = 2 end with 1506691585340
pool-1-thread-1 name = 1 end with 1506691585340
从输出结果可以看出,最多创建了3个线程,所以此方法创建的最多线程数是可以控制的。
此方法创建的线程池中只有一个线程,单一线程池可以实现以队列形式来执行任务。
示例:
创建MyRunnable类,参考以上示例。
调试代码:
public class Main {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for (int i = 0; i < 3; i++) {
service.execute(new MyRunnable((i + 1) + ""));
}
service.shutdown();
}
}
调试结果:
pool-1-thread-1 name = 1 begin with 1506692005832
pool-1-thread-1 name = 1 end with 1506692007836
pool-1-thread-1 name = 2 begin with 1506692007837
pool-1-thread-1 name = 2 end with 1506692009838
pool-1-thread-1 name = 3 begin with 1506692009838
pool-1-thread-1 name = 3 end with 1506692011841
从结果可以看出,此线程池中只有一个线程来执行任务。
除了以上三个创建线程池的方法,另外还有ThreadFactory的参数,此参数是用来对线程池中线程自定义一些特殊标识,查看ThreadFactory源码中发现,只有一个方法,如下:
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
再次就不多说了,有需要自定义的话可以实现这个接口来自定义线程。
Executors工具类中提供的几种创建线程池的方法无非是java程序的语法糖,实际底层创建线程池的还是ThreadPoolExecutor类,只是不同方法参数有所差异。
我们可以看下 Executors.newCachedThreadPool()方法的源代码,可以看到也是使用ThreadPoolExecutor来实现的多线程,具体参数是什么意思,后面会说明。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
ThreadPoolExecutor类中最常用的构造方法是:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue() queue);
参数说明:
有了上面的说明,可以还不是很理解,下面我们就从两个方面来说明如何正确创建线程池。根据参数对象BlockingQueue来学习,此参数有两个对象可传递,一个是LinkedBlockingQueue对象,另一个是SynchronousQueue对象,传递的参数不一样,对于线程池中的实现也不一样,下面我们一一说明,先来说LinkedBlockingQueue对象。
当执行的线程数threadCount <= corePoolSize 时,看以下代码:
private static void lessThanCorePoolSizeWithLinkedBlockingQueue(Runnable runnable) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(7, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue());
for (int i = 0; i < 7; i++) {
executor.execute(runnable);
}
executor.shutdown();
}
测试结果:
pool-1-thread-1 run : 1506694684337
pool-1-thread-2 run : 1506694684337
pool-1-thread-3 run : 1506694684337
pool-1-thread-4 run : 1506694684338
pool-1-thread-5 run : 1506694684338
pool-1-thread-6 run : 1506694684338
pool-1-thread-7 run : 1506694684338
从结果上可以看出,执行的线程数 threadCount <= corePoolSize时,立刻创建线程去执行任务,不放入扩展队列Queue中,其他参数忽略
当执行的线程数threadCount > corePoolSize 时,看以下代码:
public static void greaterThanCorePoolSizeWithLinkedBlockingQueue(Runnable runnable) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(7, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue());
for (int i = 0; i < 9; i++) {
executor.execute(runnable);
}
executor.shutdown();
}
测试结果:
pool-2-thread-1 run : 1506694684339
pool-2-thread-2 run : 1506694684339
pool-2-thread-3 run : 1506694684339
pool-2-thread-4 run : 1506694684339
pool-2-thread-5 run : 1506694684339
pool-2-thread-6 run : 1506694684339
pool-2-thread-7 run : 1506694684339
pool-2-thread-1 run : 1506694685340
pool-2-thread-2 run : 1506694685340
从结果中可以看到,及时执行的任务数超过了corePoolSize,创建的线程数最多是corePoolSize个数,多余的任务会放到队列中等待执行。
结论:
ThreadPoolExecutor若使用LinkedBlockingQueue作为执行任务队列,则会忽略maximumPoolSize参数设置,将 (执行线程数 - corePoolSize) 的线程放入队列中等待执行。
全部代码清单如下:
MyRunnable类,
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run : " + System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试类LinkedBlockingQueueDemo:
package threadpoolexecutordemo.linkedblockingqueuedemo;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class LinkedBlockingQueueDemo {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
createThreadPoolWithLinkedBlockingQueue(runnable);
}
/**
* ThreadPoolExecutor 使用 LinkedBlockingQueue 作为线程队列
*/
public static void createThreadPoolWithLinkedBlockingQueue(Runnable runnable) {
/**
* 执行的线程数 threadCount <= corePoolSize
* 结果:立刻创建线程去执行任务,不放入扩展队列Queue中,其他参数忽略
*/
lessThanCorePoolSizeWithLinkedBlockingQueue(runnable);
System.out.println("=====================");
/**
* 执行的线程数 threadCount > corePoolSize
* 结果:会立刻执行线程任务,生成的线程数为corePoolSize值,并且maximumPoolSize忽略,
* 将 (执行线程数 - corePoolSize) 的线程放入LinkedBlockingQueue队列中等待执行
*/
greaterThanCorePoolSizeWithLinkedBlockingQueue(runnable);
}
private static void lessThanCorePoolSizeWithLinkedBlockingQueue(Runnable runnable) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(7, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue());
for (int i = 0; i < 7; i++) {
executor.execute(runnable);
}
executor.shutdown();
}
public static void greaterThanCorePoolSizeWithLinkedBlockingQueue(Runnable runnable) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(7, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue());
for (int i = 0; i < 9; i++) {
executor.execute(runnable);
}
executor.shutdown();
}
}
当执行的线程数threadCount <= corePoolSize 时,看以下代码:
private static void lessThanCorePoolSizeWithSynchronousQueue(Runnable runnable) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(7, 10, 5, TimeUnit.SECONDS, new SynchronousQueue());
for (int i = 0; i < 7; i++) {
executor.execute(runnable);
}
executor.shutdown();
}
执行结果:
pool-1-thread-1 run : 1506695360809
pool-1-thread-4 run : 1506695360809
pool-1-thread-5 run : 1506695360809
pool-1-thread-3 run : 1506695360809
pool-1-thread-2 run : 1506695360809
pool-1-thread-6 run : 1506695360810
pool-1-thread-7 run : 1506695360810
从结果看出,当执行的线程数 threadCount <= corePoolSize,立刻创建线程去执行任务,不放入扩展队列Queue中,其他参数忽略。
当执行的线程数 threadCount > corePoolSize && threadCount <= maximumPoolSize时,代码如下:
public static void greaterThanCorePoolSizeWithSynchronousQueue(Runnable runnable) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(7, 10, 5, TimeUnit.SECONDS, new SynchronousQueue());
for (int i = 0; i < 9; i++) {
executor.execute(runnable);
}
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(executor.getCorePoolSize());
System.out.println(executor.getPoolSize());
System.out.println(executor.getQueue().size());
executor.shutdown();
}
执行结果:
pool-1-thread-2 run : 1506695459525
pool-1-thread-1 run : 1506695459525
pool-1-thread-3 run : 1506695459525
pool-1-thread-4 run : 1506695459525
pool-1-thread-5 run : 1506695459526
pool-1-thread-6 run : 1506695459526
pool-1-thread-7 run : 1506695459526
pool-1-thread-8 run : 1506695459526
pool-1-thread-9 run : 1506695459526
7
9
0
从结果可看出,当执行的线程数 threadCount > corePoolSize && threadCount <= maximumPoolSize时,会立刻执行线程任务,并不将 (执行线程数 - corePoolSize) 的线程放入SynchronousQueue列中,在执行完任务后指定时间后发生超时时将空闲线程进行清除。
当执行的线程数 threadCount > maximumPoolSize时,代码如下:
public static void greaterThanMaxPoolSizeWithSynchronousQueue(Runnable runnable) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(7, 10, 5, TimeUnit.SECONDS, new SynchronousQueue());
for (int i = 0; i < 11; i++) {
executor.execute(runnable);
}
try {
Thread.sleep(1000);
System.out.println(executor.getCorePoolSize());
System.out.println(executor.getPoolSize());
System.out.println(executor.getQueue().size());
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdownNow();
}
}
执行结果:
pool-1-thread-1 run : 1506695562414
pool-1-thread-2 run : 1506695562414
pool-1-thread-3 run : 1506695562414
pool-1-thread-4 run : 1506695562414
pool-1-thread-5 run : 1506695562414
pool-1-thread-6 run : 1506695562415
pool-1-thread-7 run : 1506695562415
pool-1-thread-8 run : 1506695562415
pool-1-thread-9 run : 1506695562415
pool-1-thread-10 run : 1506695562415
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task threadpoolexecutordemo.linkedblockingqueuedemo.MyRunnable@5ef94934 rejected from java.util.concurrent.ThreadPoolExecutor@6139cf9c[Running, pool size = 10, active threads = 10, queued tasks = 0, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
at threadpoolexecutordemo.linkedblockingqueuedemo.SynchronousQueueDemo.greaterThanMaxPoolSizeWithSynchronousQueue(SynchronousQueueDemo.java:76)
at threadpoolexecutordemo.linkedblockingqueuedemo.SynchronousQueueDemo.createThreadPoolWithSynchronousQueue(SynchronousQueueDemo.java:46)
at threadpoolexecutordemo.linkedblockingqueuedemo.SynchronousQueueDemo.main(SynchronousQueueDemo.java:16)
从结果中看出,当执行的线程数 threadCount > maximumPoolSize时,会立刻执行线程任务,但是只会处理maximumPoolSize个任务,其他任务不处理并且抛出异常,此时shutdown()方法将失效。
结论:
当ThreadPoolExecutor使用SynchronousQueue作为任务队列时,执行的线程数 最大值为maximumPoolSize,超过最大值,只会处理maximumPoolSize个任务,其他任务不处理并且抛出异常,小于最大值,则程序执行没有问题,但是并不将 (执行线程数 - corePoolSize) 的线程放入SynchronousQueue列中。
以上就是我对Executors工厂类以及ThreadPoolExecutor类的总结,希望对看官有些帮助。