Java线程池的使用

Executors类有以下几个方法
1、public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用的、具有固定线程数的线程池。
2、public static ExecutorService newSingleThreadExecutor():创建一个只有单线程的线程池,它相当于newFixedThreadPool方法是传入的参数为1
3、public static ExecutorService newCachedThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中。
4、public static ScheduledExecutorService newSingleThreadScheduledExecutor:创建只有一条线程的线程池,他可以在指定延迟后执行线程任务

5、public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,它可以再指定延迟后执行线程任务,corePoolSize指池中所保存的线程数,即使线程是空闲的也被保存在线程池内。

一、简单使用

1) 建立测试用执行类

public class PoolRunner implements Runnable {

    private final String name;

    public PoolRunner(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS").format(new Date());
        System.out.println("DateTime:" + dateTime + " Name:" + name + " ThreadName:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            System.out.println(ex.getMessage());
        }
    }
}


2)newFixedThreadPool

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        for(int i=0;i<10;i++){
            fixedThreadPool.execute(new PoolRunner("name" + i));
        }
        fixedThreadPool.shutdown();
执行结果:
DateTime:2015-06-26 10:49:33.530 Name:name3 ThreadName:pool-1-thread-4
DateTime:2015-06-26 10:49:33.530 Name:name0 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:49:33.530 Name:name4 ThreadName:pool-1-thread-5
DateTime:2015-06-26 10:49:33.530 Name:name2 ThreadName:pool-1-thread-3
DateTime:2015-06-26 10:49:33.530 Name:name1 ThreadName:pool-1-thread-2
DateTime:2015-06-26 10:49:34.530 Name:name5 ThreadName:pool-1-thread-2
DateTime:2015-06-26 10:49:34.530 Name:name6 ThreadName:pool-1-thread-4
DateTime:2015-06-26 10:49:34.530 Name:name7 ThreadName:pool-1-thread-5
DateTime:2015-06-26 10:49:34.530 Name:name8 ThreadName:pool-1-thread-3
DateTime:2015-06-26 10:49:34.530 Name:name9 ThreadName:pool-1-thread-1
可以看出线程池中总共有5个线程,每个线程执行了2次任务,间隔1秒(由于Run方法中的Sleep)


3)newSingleThreadExecutor

        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        for(int i=0;i<10;i++){
            singleThreadPool.execute(new PoolRunner("name" + i));
        }
        singleThreadPool.shutdown();
执行结果:

DateTime:2015-06-26 10:54:27.937 Name:name0 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:28.937 Name:name1 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:29.937 Name:name2 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:30.937 Name:name3 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:31.937 Name:name4 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:32.937 Name:name5 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:33.937 Name:name6 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:34.937 Name:name7 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:35.937 Name:name8 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:54:36.937 Name:name9 ThreadName:pool-1-thread-1
只有一个线程


4)newCachedThreadPool

        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for(int i=0;i<10;i++){
            cachedThreadPool.execute(new PoolRunner("name" + i));
        }
        cachedThreadPool.shutdown();
执行结果:

DateTime:2015-06-26 10:56:28.415 Name:name4 ThreadName:pool-1-thread-5
DateTime:2015-06-26 10:56:28.415 Name:name2 ThreadName:pool-1-thread-3
DateTime:2015-06-26 10:56:28.415 Name:name9 ThreadName:pool-1-thread-10
DateTime:2015-06-26 10:56:28.415 Name:name6 ThreadName:pool-1-thread-7
DateTime:2015-06-26 10:56:28.415 Name:name0 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:56:28.415 Name:name3 ThreadName:pool-1-thread-4
DateTime:2015-06-26 10:56:28.415 Name:name1 ThreadName:pool-1-thread-2
DateTime:2015-06-26 10:56:28.415 Name:name7 ThreadName:pool-1-thread-8
DateTime:2015-06-26 10:56:28.415 Name:name5 ThreadName:pool-1-thread-6
DateTime:2015-06-26 10:56:28.415 Name:name8 ThreadName:pool-1-thread-9
总共建立了10个线程


5)newScheduledThreadPool

        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 10; i++) {
            String name = "name" + i;
            String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS").format(new Date());
            System.out.println("ScheduledTime:" + dateTime + " Name:" + name);
            scheduledThreadPool.schedule(new PoolRunner(name), 1, TimeUnit.SECONDS);
        }
        scheduledThreadPool.shutdown();

schedule方法的第二个参数代表延迟多少时间执行,时间单位由最后一个参数决定

执行结果:

ScheduledTime:2015-06-26 10:59:12.576 Name:name0
ScheduledTime:2015-06-26 10:59:12.576 Name:name1
ScheduledTime:2015-06-26 10:59:12.576 Name:name2
ScheduledTime:2015-06-26 10:59:12.576 Name:name3
ScheduledTime:2015-06-26 10:59:12.576 Name:name4
ScheduledTime:2015-06-26 10:59:12.576 Name:name5
ScheduledTime:2015-06-26 10:59:12.576 Name:name6
ScheduledTime:2015-06-26 10:59:12.576 Name:name7
ScheduledTime:2015-06-26 10:59:12.576 Name:name8
ScheduledTime:2015-06-26 10:59:12.576 Name:name9
DateTime:2015-06-26 10:59:13.586 Name:name0 ThreadName:pool-1-thread-4
DateTime:2015-06-26 10:59:13.586 Name:name3 ThreadName:pool-1-thread-3
DateTime:2015-06-26 10:59:13.586 Name:name2 ThreadName:pool-1-thread-2
DateTime:2015-06-26 10:59:13.586 Name:name1 ThreadName:pool-1-thread-5
DateTime:2015-06-26 10:59:13.586 Name:name4 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:59:14.586 Name:name5 ThreadName:pool-1-thread-1
DateTime:2015-06-26 10:59:14.586 Name:name7 ThreadName:pool-1-thread-3
DateTime:2015-06-26 10:59:14.586 Name:name6 ThreadName:pool-1-thread-5
DateTime:2015-06-26 10:59:14.586 Name:name8 ThreadName:pool-1-thread-2
DateTime:2015-06-26 10:59:14.586 Name:name9 ThreadName:pool-1-thread-4
创建了保持5个线程的线程池,线程执行延迟1秒执行

6)newSingleThreadScheduledExecutor

ScheduledExecutorService singleScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
        for (int i = 0; i < 10; i++) {
            String name = "name" + i;
            String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS").format(new Date());
            System.out.println("ScheduledTime:" + dateTime + " Name:" + name);
            singleScheduledThreadPool.schedule(new PoolRunner(name), 1, TimeUnit.SECONDS);
        }
        singleScheduledThreadPool.shutdown();
执行结果:

ScheduledTime:2015-06-26 11:02:06.652 Name:name0
ScheduledTime:2015-06-26 11:02:06.662 Name:name1
ScheduledTime:2015-06-26 11:02:06.662 Name:name2
ScheduledTime:2015-06-26 11:02:06.662 Name:name3
ScheduledTime:2015-06-26 11:02:06.662 Name:name4
ScheduledTime:2015-06-26 11:02:06.662 Name:name5
ScheduledTime:2015-06-26 11:02:06.662 Name:name6
ScheduledTime:2015-06-26 11:02:06.662 Name:name7
ScheduledTime:2015-06-26 11:02:06.662 Name:name8
ScheduledTime:2015-06-26 11:02:06.662 Name:name9
DateTime:2015-06-26 11:02:07.672 Name:name0 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:08.674 Name:name1 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:09.674 Name:name2 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:10.674 Name:name3 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:11.678 Name:name4 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:12.678 Name:name5 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:13.679 Name:name6 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:14.686 Name:name7 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:15.686 Name:name8 ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:02:16.688 Name:name9 ThreadName:pool-1-thread-1
延迟1秒执行,但是只有一个单独的线程

二、ThreadFactory

所以线程池的初始化方法都可以自定义ThreadFactory

这样就可以在初始化线程的时候做一些设定,比如使用setUncaughtExceptionHandler对异常进行处理、写入Log。

实现一个更改线程名的ThreadFactory作为测试:

public class ErrorRunner implements Runnable  {

    @Override
    public void run() {
        throw new RuntimeException("Thread Exception Test."); 
    } 
}

public class MyThreadFactory implements ThreadFactory {

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        thread.setUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl());
        return thread;
    }

    private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println(t.getName() + " error:" + e.getMessage());
        }
    }
}
创建线程池:

        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(new MyThreadFactory());
        singleThreadPool.execute(new ErrorRunner());
        singleThreadPool.shutdown();
执行结果:

run:
Thread-0 error:Thread Exception Test.

三、定时计划循环执行
1)scheduleAtFixedRate和scheduleWithFixedDelay的区别

scheduleAtFixedRate严格按照指定的时间间隔执行任务。如果任务的执行时间大于指定的时间间隔,则在上一个任务结束后直接启动下一个任务

scheduleWithFixedDelay在上一个任务结束以后,再等待指定的间隔时间,才会启动下一个任务

示例:

2)scheduleAtFixedRate

        ScheduledExecutorService singleScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
        String name = "LoopTest";
        String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS").format(new Date());
        System.out.println("ScheduledTime:" + dateTime + " Name:" + name);
        singleScheduledThreadPool.scheduleAtFixedRate(new PoolRunner(name), 1, 2, TimeUnit.SECONDS);
        Thread.sleep(10000);
        System.out.println("ShutDown!");
        singleScheduledThreadPool.shutdown();
scheduleAtFixedRate第三个参数代表设定的时间间隔

注意上面建立的PoolRunner类的Run方法中会Sleep 1秒

执行结果:

run:
ScheduledTime:2015-06-26 11:49:45.752 Name:LoopTest
DateTime:2015-06-26 11:49:46.744 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:49:48.767 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:49:50.767 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:49:52.767 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:49:54.767 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:49:56.767 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:49:58.768 Name:LoopTest ThreadName:pool-1-thread-1
ShutDown!
每次执行间隔2秒钟

PoolRunner类的Run方法中会Sleep改为3秒后再次执行

执行结果:

run:
ScheduledTime:2015-06-26 11:52:32.625 Name:LoopTest
DateTime:2015-06-26 11:52:33.635 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:52:36.636 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:52:39.641 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:52:42.647 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:52:45.647 Name:LoopTest ThreadName:pool-1-thread-1
ShutDown!
执行间隔变为3秒

3)scheduleWithFixedDelay

PoolRunner类的Run方法中会Sleep改1秒后执行如下代码

        ScheduledExecutorService singleScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
        String name = "LoopTest";
        String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS").format(new Date());
        System.out.println("ScheduledTime:" + dateTime + " Name:" + name);
        singleScheduledThreadPool.scheduleWithFixedDelay(new PoolRunner(name), 1, 2, TimeUnit.SECONDS);
        Thread.sleep(10000);
        System.out.println("ShutDown!");
        singleScheduledThreadPool.shutdown();
执行结果:

run:
ScheduledTime:2015-06-26 11:54:50.718 Name:LoopTest
DateTime:2015-06-26 11:54:51.726 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:54:54.733 Name:LoopTest ThreadName:pool-1-thread-1
DateTime:2015-06-26 11:54:57.743 Name:LoopTest ThreadName:pool-1-thread-1
ShutDown!

可以看到执行间隔依然为3秒,因为scheduleWithFixedDelay方法会在每次任务执行结束(耗时1秒)后再等待2秒,才会启动下一次任务


4. submit 方法

Callable中的call()方法类似Runnable的run()方法。但是call()方法有返回值,run()方法没有。

使用submit方法,就可以从方法返回的Future对象中取得返回值的内容。

示例:

public class PoolCaller implements Callable<String> {

    private final String name;

    public PoolCaller(String name) {
        this.name = name;
    }

    @Override
    public String call() throws Exception {
        String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS").format(new Date());
        String result = "DateTime:" + dateTime + " Name:" + name + " ThreadName:" + Thread.currentThread().getName();
        Thread.sleep(1000);
        return result;
    }
}
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        Future<String> submit = singleThreadPool.submit(new PoolCaller("SubmitTest"));
        System.out.println(System.currentTimeMillis());
        System.out.println(submit.get());
        System.out.println(System.currentTimeMillis());
        singleThreadPool.shutdown();
执行结果:

run:
1435297292530
DateTime:2015-06-26 01:41:32.560 Name:SubmitTest ThreadName:pool-1-thread-1
1435297293560
可以看出来,Future的get方法会等待任务执行完成,然后返回call方法的返回值。

Future<?> submit(Runnable task)返回的Future对象调用get时会返回null

<T> Future<T> submit(Runnable task, T result)方法则会把传入的result的对象再返回出来


5. invokeAll方法

当要执行多个Callable的时候则可以使用invokeAll方法,它会在所有任务执行完以后返回一个Future的List

示例:

public class PoolCaller implements Callable<String> {

    private final String name;

    public PoolCaller(String name) {
        this.name = name;
    }

    @Override
    public String call() throws Exception {
        String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS").format(new Date());
        String result = "DateTime:" + dateTime + " Name:" + name + " ThreadName:" + Thread.currentThread().getName();
        System.out.println(result);
        Thread.sleep(1000);
        return name + " is done!";
    }
}
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        List<PoolCaller> list = new ArrayList(){{
            add(new PoolCaller("invokeAll-1"));
            add(new PoolCaller("invokeAll-2"));
        }};
        List<Future<String>> result = singleThreadPool.invokeAll(list);
        for(Future<String> future: result){
            System.out.println(future.get());
        }
        singleThreadPool.shutdown();
执行结果:

run:
DateTime:2015-06-26 02:08:46.309 Name:invokeAll-1 ThreadName:pool-1-thread-1
DateTime:2015-06-26 02:08:47.309 Name:invokeAll-2 ThreadName:pool-1-thread-1
invokeAll-1 is done!
invokeAll-2 is done!

另外invokeAll方法还有一个设置timout的重载,如果超过时间则会将已经完成的task返回,未完成的取消。

invokeAny方法则会任意执行传入的一组task中的任意一个。


你可能感兴趣的:(Java线程池的使用)