zoukankan      html  css  js  c++  java
  • Java中常用的四种线程池

    摘自:https://www.cnblogs.com/heihaozi/p/11741735.html

    随笔 - 26  文章 - 0  评论 - 9

    Java中常用的四种线程池

     

    在Java中使用线程池,可以用ThreadPoolExecutor的构造函数直接创建出线程池实例,如何使用参见之前的文章Java线程池构造参数详解。不过,在Executors类中,为我们提供了常用线程池的创建方法。接下来我们就来了解常用的四种:

    newFixedThreadPool

    首先,看一下这种线程池的创建方法:

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

    从构造方法可以看出,它创建了一个固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大值nThreads。线程池的大小一旦达到最大值后,再有新的任务提交时则放入无界阻塞队列中,等到有线程空闲时,再从队列中取出任务继续执行。
    那么,如何使用newFixedThreadPool呢?我们来举个例子:

    public class OneMoreStudy {
        public static void main(String[] args) {
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    
            for (int i = 0; i < 5; i++) {
                final int index = i;
                fixedThreadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                SimpleDateFormat sdf = new SimpleDateFormat(
                                        "HH:mm:ss");
                                System.out.println("运行时间: " +
                                    sdf.format(new Date()) + " " + index);
                                Thread.sleep(2000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    });
            }
    
            fixedThreadPool.shutdown(); 
        }
    }

    上面的例子中创建了一个固定大小为3的线程池,然后在线程池提交了5个任务。在提交第4个任务时,因为线程池的大小已经达到了3并且前3个任务在运行中,所以第4个任务被放入了队列,等待有空闲的线程时再被运行。运行结果如下(注意前3个任务和后2个任务的运行时间):

    运行时间: 08:09:02 1
    运行时间: 08:09:02 2
    运行时间: 08:09:02 0
    运行时间: 08:09:04 4
    运行时间: 08:09:04 3

    newCachedThreadPool

    首先,看一下这种线程池的创建方法:

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

    从构造方法可以看出,它创建了一个可缓存的线程池。当有新的任务提交时,有空闲线程则直接处理任务,没有空闲线程则创建新的线程处理任务,队列中不储存任务。线程池不对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。如果线程空闲时间超过了60秒就会被回收。
    那么,如何使用newCachedThreadPool呢?我们来举个例子:

    public class OneMoreStudy {
        public static void main(String[] args) {
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    
            for (int i = 0; i < 5; i++) {
                final int index = i;
                cachedThreadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                SimpleDateFormat sdf = new SimpleDateFormat(
                                        "HH:mm:ss");
                                System.out.println("运行时间: " +
                                    sdf.format(new Date()) + " " + index);
                                Thread.sleep(2000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    });
            }
    
            cachedThreadPool.shutdown();
        }
    }

    因为这种线程有新的任务提交,就会创建新的线程(线程池中没有空闲线程时),不需要等待,所以提交的5个任务的运行时间是一样的,运行结果如下:

    运行时间: 08:45:18 2
    运行时间: 08:45:18 1
    运行时间: 08:45:18 3
    运行时间: 08:45:18 4
    运行时间: 08:45:18 0

    newSingleThreadExecutor

    首先,看一下这种线程池的创建方法:

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

    从构造方法可以看出,它创建了一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。
    那么,如何使用newSingleThreadExecutor呢?我们来举个例子:

    public class OneMoreStudy {
        public static void main(String[] args) {
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    
            for (int i = 0; i < 5; i++) {
                final int index = i;
                singleThreadExecutor.execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                SimpleDateFormat sdf = new SimpleDateFormat(
                                        "HH:mm:ss");
                                System.out.println("运行时间: " +
                                    sdf.format(new Date()) + " " + index);
                                Thread.sleep(2000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    });
            }
    
            singleThreadExecutor.shutdown();
        }
    }

    因为该线程池类似于单线程执行,所以先执行完前一个任务后,再顺序执行下一个任务,
    运行结果如下:

    运行时间: 08:54:17 0
    运行时间: 08:54:19 1
    运行时间: 08:54:21 2
    运行时间: 08:54:23 3
    运行时间: 08:54:25 4

    有的同学可能会质疑:既然类似于单线程执行,那么这种线程池还有存在的必要吗?这里的单线程执行指的是线程池内部,从线程池外的角度看,主线程在提交任务到线程池时并没有阻塞,仍然是异步的。

    newScheduledThreadPool

    这个方法创建了一个固定大小的线程池,支持定时及周期性任务执行。
    首先看一下定时执行的例子:

    public class OneMoreStudy {
        public static void main(String[] args) {
            final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
            System.out.println("提交时间: " + sdf.format(new Date()));
            scheduledThreadPool.schedule(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("运行时间: " + sdf.format(new Date()));
                    }
                }, 3, TimeUnit.SECONDS);
            scheduledThreadPool.shutdown();
        }
    }

    使用该线程池的schedule方法,延迟3秒钟后执行任务,运行结果如下:

    提交时间: 09:11:39
    运行时间: 09:11:42

    再看一下周期执行的例子:

    public class OneMoreStudy {
        public static void main(String[] args) {
            final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
            System.out.println("提交时间: " + sdf.format(new Date()));
            scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("运行时间: " + sdf.format(new Date()));
                    }
                }, 1, 3, TimeUnit.SECONDS);
            Thread.sleep(10000);
            scheduledThreadPool.shutdown();
        }
    }

    使用该线程池的scheduleAtFixedRate方法,延迟1秒钟后每隔3秒执行一次任务,运行结果如下:

    提交时间: 09:23:20
    运行时间: 09:23:21
    运行时间: 09:23:24
    运行时间: 09:23:27
    作者:万猫学社
    出处:http://www.cnblogs.com/heihaozi/
    微信扫描二维码,关注万猫学社,获得更多Java技术干货。
     
    分类: 技术
  • 相关阅读:
    Eclipse 导入项目乱码问题(中文乱码)
    sql中视图视图的作用
    Java基础-super关键字与this关键字
    Android LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)的参数理解
    Android View和ViewGroup
    工厂方法模式(java 设计模式)
    设计模式(java) 单例模式 单例类
    eclipse乱码解决方法
    No resource found that matches the given name 'Theme.AppCompat.Light 的完美解决方案
    【转】使用 Eclipse 调试 Java 程序的 10 个技巧
  • 原文地址:https://www.cnblogs.com/xichji/p/11757110.html
Copyright © 2011-2022 走看看