在JDK1.5之前,如果需要使用线程池,需要自己动手写代码实现,这对于初学者来说,不是一件容易的事。JDK1.5提供了Executor类,用户通过它可得到各种类型的线程池。
Executor提供的线程池,可以分为:固定尺寸的线程池、可变尺寸的线程池。
Executor.java提供部分接口
public static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 public static ExecutorService newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 public static ExecutorService newCachedThreadPool() 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 public static ScheduledExecutorService newSingleThreadScheduledExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
public static ExecutorService newFixedThreadPool(int n),线程池中有n个线程,如果实际线程数m,m>n,则先加入的前n个线程,先执行,处于执行状态,后加入的m-n个线程,处于阻塞状态,直到有某个线程执行完了,则处于阻塞状态的线程中的其中一个启动。也就是说最多同时n个线程处于执行状态。
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService executorService=Executors.newFixedThreadPool(3);
MyThread a=new MyThread("A");
MyThread b=new MyThread("B");
MyThread c=new MyThread("C");
MyThread d=new MyThread("D");
executorService.execute(a);
executorService.execute(b);
executorService.execute(c);
executorService.execute(d);
executorService.shutdown();
}
static class MyThread implements Runnable{
private String name;
public MyThread(String name){
this.name=name;
}
@Override
public void run(){
for(int i=1;i<=5;i++){
System.out.println(this.name+"第"+i+"次运行.........");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
运行结果
B第1次运行......... C第1次运行......... A第1次运行......... B第2次运行......... C第2次运行......... A第2次运行......... B第3次运行......... C第3次运行......... A第3次运行......... B第4次运行......... C第4次运行......... A第4次运行......... B第5次运行......... C第5次运行......... A第5次运行......... D第1次运行......... D第2次运行......... D第3次运行......... D第4次运行......... D第5次运行.........
ABC同时启动,所以打印顺序是混乱的;最后有空闲线程资源执行D时,只有它在执行,所以看上去有序。
public static ExecutorService newSingleThreadExecutor()线程池中的线程数为1,如果实际线程数大于1时,则按照加入的顺序按顺序执行。所以如果开发中遇到多个线程一个个执行(前一个执行完后,后一个才能执行)的话,可以使用这种方式就可以避免使用join()方法,一个个控制了。
将上面程序获得线程池的语句修改一下。
// ExecutorService executorService=Executors.newFixedThreadPool(3);
ExecutorService executorService=Executors.newSingleThreadExecutor();
运行结果A第1次运行......... A第2次运行......... A第3次运行......... A第4次运行......... A第5次运行......... B第1次运行......... B第2次运行......... B第3次运行......... B第4次运行......... B第5次运行......... C第1次运行......... C第2次运行......... C第3次运行......... C第4次运行......... C第5次运行......... D第1次运行......... D第2次运行......... D第3次运行......... D第4次运行......... D第5次运行.........
运行结果是有序的。
public static ExecutorService newCachedThreadPool()动态创建线程数,如果新加入一个线程任务,当前线程池中没有空闲的线程,则新创建一个线程用来执行新任务;如果有空闲线程,则使用空闲线程。一个线程的任务执行完后,线程状态成为空闲状态,线程不会立即销毁,60秒后,如果没有新任务进来,则空闲线程(空闲时间已达到60s)就销毁.
将上面程序获取线程池的语句修改下。
// ExecutorService executorService=Executors.newFixedThreadPool(3);
// ExecutorService executorService=Executors.newSingleThreadExecutor();
ExecutorService executorService=Executors.newCachedThreadPool();
运行结果B第1次运行......... D第1次运行......... A第1次运行......... C第1次运行......... B第2次运行......... D第2次运行......... A第2次运行......... C第2次运行......... B第3次运行......... D第3次运行......... A第3次运行......... C第3次运行......... D第4次运行......... B第4次运行......... A第4次运行......... C第4次运行......... B第5次运行......... D第5次运行......... A第5次运行......... C第5次运行.........
ABCD的运行是混乱的。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize),看方法名就知道,该线程池可以执行定时任务。
package thread;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
public static void main(String[] args) {
ScheduledExecutorService executorService=Executors.newScheduledThreadPool(3);
MyThread a=new MyThread("A");
MyThread b=new MyThread("B");
MyThread c=new MyThread("C");
MyThread d=new MyThread("D");
executorService.schedule(a, 1000, TimeUnit.MILLISECONDS);//1000毫秒后执行
executorService.schedule(b, 500, TimeUnit.MILLISECONDS);//500ms后执行
executorService.schedule(c, 1, TimeUnit.SECONDS);//1s后执行
executorService.schedule(d, 1, TimeUnit.SECONDS);
executorService.shutdown();
}
static class MyThread implements Runnable{
private String name;
public MyThread(String name){
this.name=name;
}
@Override
public void run(){
for(int i=1;i<=3;i++)
System.out.println(name+"在执行第"+i+"次....."+new SimpleDateFormat("hh:mm:ss").format(new Date()));
}
}
}
运行结果
B在执行第1次.....08:20:16 B在执行第2次.....08:20:16 B在执行第3次.....08:20:16 A在执行第1次.....08:20:17 A在执行第2次.....08:20:17 A在执行第3次.....08:20:17 C在执行第1次.....08:20:17 C在执行第2次.....08:20:17 C在执行第3次.....08:20:17 D在执行第1次.....08:20:17 D在执行第2次.....08:20:17 D在执行第3次.....08:20:17
至于public static ScheduledExecutorService newSingleThreadScheduledExecutor(),读者可以将上面程序修改下就OK了。
运行结果
B在执行第1次.....08:25:08 B在执行第2次.....08:25:08 B在执行第3次.....08:25:08 A在执行第1次.....08:25:09 A在执行第2次.....08:25:09 A在执行第3次.....08:25:09 C在执行第1次.....08:25:09 C在执行第2次.....08:25:09 C在执行第3次.....08:25:09 D在执行第1次.....08:25:09 D在执行第2次.....08:25:09 D在执行第3次.....08:25:09