Executor的使用

释放双眼,带上耳机,听听看~!

在上一篇我们简单的介绍了线程池的使用,虽然线程池比我们直接使用原始的线程类更加方便,但在创建线程池对象时对参数的设置是需要开发人员精心考量的,否则线程池并不会实现我们满意的效果。在实际的开发中,我们用的最多的线程框架其实不是线程池而是Executor。它为我们创建一个线程池提供了更方便的方法。Executor是一个接口,它是Executor框架的基础,它使任务的提交与任务的执行分离。底层仍然使用的是线程池。通过Executor框架的工具类Executors,可以创建3种类型的线程池它们分别是:FixedThreadPool、SingleThreadExecutor、CachedThreadPool。下面我们分别看一下它们之间的区别。

FixedThreadPool:可重用固定线程数的线程池。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

我们从源码中可以看出newFixedThreadPool()方法将线程池中的初始化的线程数和允许最大的线程数都设置成了参数值。也就是说我们参数传递的是多少就同时有多少个线程在同时执行。并且它又创建了LinkedBlockingQueue来实现线程池中的队列服务。我们知道LinkedBlockingQueue是一个无界队列,也就是说它可以存储很多个线程任务,当线程池中的并发任务大于线程池中允许的最大数时,就会将任务保存到此队列中。当有线程执行完毕后,线程池在从队列中取出相应的任务去执行。并且我发们发现newFixedThreadPool()方法中已经将keepAliveTime设置为0,目的是如果有线程执行完任务后,要求立即终止。

SingleThreadExecutor:单个线程的线程池。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

我们看出SingleThreadExecutor()和newFixedThreadPool()方法几乎一样,只是设置了线程池的初始化线程数和允许的最大线程数都是1。执行的过程和上诉的逻辑一样。

CachedThreadPool:根据需要创建新线程的线程池。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

我们看CachedThreadPool()将corePool设置为0,把maximumPoolSize设置最Integer.MAX_VALUE。意思是在创建这个线程池时,没有初始化空闲线程,而是如果有任务添加,那么它就会创建一个新线程。

下面我们看一下具体的代码。

FixedThreadPool

public class Test {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务一:%s", Thread.currentThread().getName()));
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务二:%s", Thread.currentThread().getName()));
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务三:%s", Thread.currentThread().getName()));
            }
        });
    }
}
任务一:pool-1-thread-1
任务二:pool-1-thread-2
任务三:pool-1-thread-1

因为我们只创建了2个线程,所以这个线程池的最大并发数就是2,所以任务一任务二是先同时输出的,任务三是后输出的。因为任务三被添加到了队列中,只有其它任务执行完,才会执行队列中的任务。

SingleThreadExecutor

public class Test {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务一:%s", Thread.currentThread().getName()));
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务二:%s", Thread.currentThread().getName()));
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务三:%s", Thread.currentThread().getName()));
            }
        });
    }
}
任务一:pool-1-thread-1
任务二:pool-1-thread-1
任务三:pool-1-thread-1

因为SingleThreadExecutor线程池最大的并发数是1,所以当我们提交3个任务时,只有一个会执行另外两个会被添加到队列中,所以执行的结果显示只有一个线程。

CachedThreadPool

public class Test {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务一:%s", Thread.currentThread().getName()));
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务二:%s", Thread.currentThread().getName()));
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务三:%s", Thread.currentThread().getName()));
            }
        });
    }
}
任务三:pool-1-thread-3
任务一:pool-1-thread-1
任务二:pool-1-thread-2

我们知道CachedThreadPool线程池中初始化的空闲线程是0,但是它允许的最大线程数是Integer.MAX_VALUE也就相当于无限大。所以我们在提交任务时,因为没有超过允许的最大线程数所以线程池就会为我们创建一个新线程。所以任务一任务二任务三都是并行执行的。

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧