精华内容
下载资源
问答
  • java的四种线程池
    千次阅读
    2021-06-15 11:25:08
    Java中四种具有不同功能常见的线程池。他们都是直接或者间接配置 ThreadPoolExecutor来实现他们各自的功能。这四种线程池分别是newFixedThreadPool, newCachedThreadPool ,
    newScheduledThreadPool newSingleThreadExecutor 。这四个线程池可以通过 Executors 类获取。
    1. newFixedThreadPool
    通过 Executors 中的 newFixedThreadPool 方法来创建,该线程池是一种线程数量固定的线程池。
    ExecutorService service = Executors.newFixedThreadPool(4);

    在这个线程池中,所容纳的最大线程数就是就是设置的核心线程数。如果线程池中的线程处于空闲状态的话,并不会被回收,除非是这个线程池被关闭。如果所有的线程都处于活动状态的话,新任务就会处于等待状态,直到有线程空闲出来。

    2. newCachedThreadPool
    通过 Executors 中的 newCachedThreadPool 方法来创建。
    public static ExecutorService newCachedThreadPool() 
    { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L,
     TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); 
    }
           通过 上面的 newCachedThreadPool 方法在这里我们可以看出它的核心线程数为 0 , 线程池的最大线程数 Integer.MAX_VALUE 。而 Integer.MAX_VALUE 是一个很大的数,也差不多可以说 这个线程池中的最大线程数可以任意大。
           当线程池中的线程都处于活动状态的时候,线程池就会创建一个新的线程来处理任务。该线程池中的线程超时时长为60 秒,所以当线程处于闲置状态超过 60 秒的时候便会被回收。 这也就意味着若是整个线程池的线程都处于闲置状态超过60 秒以后,在newCachedThreadPool 线程池中是不存在任何线程的,所以这时候它几乎不占用任何的系统资源。
     
    3. newScheduledThreadPool
    通过 Executors 中的 newScheduledThreadPool 方法来创建。
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { 
    return new ScheduledThreadPoolExecutor(corePoolSize); 
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) { 
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, 
    new DelayedWorkQueue()); 
    }
    它的核心线程数是固定的,对于非核心线程几乎可以说是没有限制的,并且当非核心线程处于限制状态的时候就会立即被回收。
    创建一个可定时执行或周期执行任务的线程池:
    ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
            service.schedule(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "延迟三秒执行");
                }
            }, 3, TimeUnit.SECONDS);
            service.scheduleAtFixedRate(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "延迟三秒后每隔2秒执行");
                }
            }, 3, 2, TimeUnit.SECONDS);
    输出结果:
    pool-1-thread-2 延迟三秒后每隔 2 秒执行
    pool-1-thread-1 延迟三秒执行
    pool-1-thread-1 延迟三秒后每隔 2 秒执行
    pool-1-thread-2 延迟三秒后每隔 2 秒执行
    pool-1-thread-2 延迟三秒后每隔 2 秒执行
     
    schedule(Runnable command, long delay, TimeUnit unit) :延迟一定时间后执行Runnable 任务;
    schedule(Callable callable, long delay, TimeUnit unit) :延迟一定时间后执行Callable 任务;
    scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) :延迟一定时间后,以间隔 period 时间的频率周期性地执行任务;
    scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit) : scheduleAtFixedRate() 方法很类似,但是不同的是scheduleWithFixedDelay()方法的周期时间间隔是以上一个任务执行结束到下一个 任务开始执行的间隔,而scheduleAtFixedRate() 方法的周期时间间隔是以上一个任务开始执行到下一个任务开始执行的间隔,也就是这一些任务系列的触发时间都是可预知的。
    ScheduledExecutorService 功能强大,对于定时执行的任务,建议多采用该方法。
     
    4. newSingleThreadExecutor
     
    通过 Executors 中的 newSingleThreadExecutor 方法来创建,在这个线程池中只有一个核心线程,对于任务队列没有大小限制,也就意味着这一个任务处于活动状态时,其他任务都会在任务队列中排队等候依次执行。
    newSingleThreadExecutor 将所有的外界任务统一到一个线程中支持,所以在这个任务执行之间我们不需要处理线程同步的问题。
    public static ExecutorService newSingleThreadExecutor(){
                return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
            }

     

     
    更多相关内容
  • java 四种线程池实例

    2018-06-06 16:01:55
    执行一个异步任务你还只是如下new Thread吗?是不是太low 了一点? 我这里有四种线程池的案例轻松让你理解和使用线程池
  • Java 中,线程池的顶级接口是 Executor,但它并不是线程池的具体实现,真正的线程池实现类为 ThreadPoolExecutor。 我们可以向线程池中传递任务以获得执行,可传递的任务有以下两,分别是通过 Runnable 实现的...
  • 本篇文章主要介绍了Java四种线程池的使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Java 4种线程池的使用

    2020-12-22 23:49:50
    Java通过Executors提供四种线程池,分别为:  newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。  newFixedThreadPool 创建一个定长线程池...
  • PAGE / NUMPAGES Java四种线程池的简单介绍 在传统的cs或者bs模式中一个线程或者一个进程负责一个请求一个请求使用一个socket来进行传输这样的模式在低并发量的情况下还是可行的但是用户量一大的话就会导致系统资源...
  • Java四种线程池使用

    万次阅读 多人点赞 2018-06-08 15:29:02
    Java 四种线程池的使用https://juejin.im/post/59df0c1af265da432f301c8d1,线程池的作用 线程池作用就是限制系统中执行线程的数量。 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。 少...

    Java 四种线程池的使用

    https://juejin.im/post/59df0c1af265da432f301c8d

    1,线程池的作用 
    线程池作用就是限制系统中执行线程的数量。 
    根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。 
    少了浪费了系统资源,多了造成系统拥挤效率不高。 
    用线程池控制线程数量,其他线程排 队等候。 
    一个任务执行完毕,再从队列的中取最前面的任务开始执行。 
    若队列中没有等待进程,线程池的这一资源处于等待。 
    当一个新任务需要运行时,如果线程池 中有等待的工作线程,就可以开始运行了;否则进入等待队列。 
    2,为什么要用线程池? 
    1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。 
    2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。 
    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。 
    3,比较重要的几个类

    类 
    描述

    ExecutorService 
    真正的线程池接口。

    ScheduledExecutorService 
    能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

    ThreadPoolExecutor 
    ExecutorService的默认实现。

    ScheduledThreadPoolExecutor 
    继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

    4,new Thread的弊端

    public class TestNewThread {
    
        public static void main(String[] args) {
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    System.out.println("start");
                }
            }).start();
        }
    }

    执行一个异步任务你还只是如下new Thread吗? 
    那你就out太多了,new Thread的弊端如下: 
    1.每次new Thread新建对象性能差。 
    2.线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。 
    3.缺乏更多功能,如定时执行、定期执行、线程中断。 
    相比new Thread,Java提供的四种线程池的好处在于: 
    1.重用存在的线程,减少对象创建、消亡的开销,性能佳。 
    2.可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。 
    3.提供定时执行、定期执行、单线程、并发数控制等功能。

    四种线程池 
    Java通过Executors提供四种线程池,分别为: 
    1,newCachedThreadPoo 
    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 
    2,newFixedThreadPool 
    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 
    3,newScheduledThreadPool 
    创建一个定长线程池,支持定时及周期性任务执行。 
    4,newSingleThreadExecutor 
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    源码分析

    newCachedThreadPool 
    这是一个可缓存线程池,可以灵活的回收空闲线程,无可回收线程时,新建线程 
    public static ExecutorService newCachedThreadPool() { 
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 
    60L, TimeUnit.SECONDS, 
    new SynchronousQueue()); 
    }码可以看出底层调用的是ThreadPoolExecutor方法,传入一个同步的阻塞队列实现

     ThreadPoolExecupublic ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

    通过源码可以看出,我们可以传入线程池的核心线程数(最小线程数),最大线程数量,保持时间,时间单位,阻塞队列这些参数,最大线程数设置为jvm可用的cpu数量为最佳实践

    newWorkStealingPool 
    创建持有足够线程的线程池来并行,通过使用多个队列减少竞争,不传参数,则默认设定为cpu的数量 
    源码:

     public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    通过源码可以看出底层调用的是ForkJoinPool线程池

    下面说一下ForkJoinPool

     public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
                        boolean asyncMode) {
        this(checkParallelism(parallelism),
             checkFactory(factory),
             handler,
             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
             "ForkJoinPool-" + nextPoolId() + "-worker-");
        checkPermission();
    }

    使用一个无限队列来保存需要执行的任务,可以传入线程的数量,不传入,则默认使用当前计算机中可用的cpu数量,使用分治法来解决问题,使用fork()和join()来进行调用

    newSingleThreadExecutor 
    创建一个单线程化的线程池,保证所有任务按照指定的顺序执行(FIFO,LIFO,优先级),当要求进程限制时,可以进行使用

    源码:

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

    newFixedThreadPool 
    创建一个固定线程数量,可重用的线程池

    源码:

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

    newScheduledThreadPool 
    创建一个可定期或者延时执行任务的线程池

    源码:

    return new ScheduledThreadPoolExecutor(corePoolSize); 

    通过源码可以看出底层调用的是一个ScheduledThreadPoolExecutor,然后传入线程数量

    下面来介绍一下ScheduledThreadPoolExecutor

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

    通过源码可以看出底层调用了ThreadPoolExecutor,维护了一个延迟队列,可以传入线程数量,传入延时的时间等参数,下面给出一个demo

     public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 15; i = i + 5) {
            pool.schedule(() -> System.out.println("我被执行了,当前时间" + new Date()), i, TimeUnit.SECONDS);
        }
        pool.shutdown();
    }

    执行结果

    我被执行了,当前时间Fri Jan 12 11:20:41 CST 2018 
    我被执行了,当前时间Fri Jan 12 11:20:46 CST 2018 
    我被执行了,当前时间Fri Jan 12 11:20:51 CST 2018

    有的小伙伴可能会用疑问,为什么使用schedule()而不使用submit()或者execute()呢,下面通过源码来分析

     public void execute(Runnable command) {
        schedule(command, 0, NANOSECONDS);
    }
    public Future<?> submit(Runnable task) {
        return schedule(task, 0, NANOSECONDS);
    }

    通过源码可以发现这两个方法都是调用的schedule(),而且将延时时间设置为了0,所以想要实现延时操作,需要直接调用schedule()

    下面我们再来分析一下submit()和execute()的以及shutdown()和shutdownNow()的区别

    submit(),提交一个线程任务,可以接受回调函数的返回值吗,适用于需要处理返回着或者异常的业务场景 
    execute(),执行一个任务,没有返回值 
    shutdown(),表示不再接受新任务,但不会强行终止已经提交或者正在执行中的任务 
    shutdownNow(),对于尚未执行的任务全部取消,正在执行的任务全部发出interrupt(),停止执行 
    五种线程池的适应场景 
    newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务。 
    newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制。 
    newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务,并且在任意时间点,不会有多个线程是活动的场景。 
    newScheduledThreadPool:可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。 
    newWorkStealingPool:创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行

    RejectedExecutionHandler 线程池四种拒绝任务策略

    《Java线程池》:任务拒绝策略 
    在没有分析线程池原理之前先来分析下为什么有任务拒绝的情况发生。

    这里先假设一个前提:线程池有一个任务队列,用于缓存所有待处理的任务,正在处理的任务将从任务队列中移除。因此在任务队列长度有限的情况下就会出现新任务的拒绝处理问题,需要有一种策略来处理应该加入任务队列却因为队列已满无法加入的情况。另外在线程池关闭的时候也需要对任务加入队列操作进行额外的协调处理。

    RejectedExecutionHandler提供了四种方式来处理任务拒绝策略

    1、直接丢弃(DiscardPolicy)

    2、丢弃队列中最老的任务(DiscardOldestPolicy)。

    3、抛异常(AbortPolicy)

    4、将任务分给调用线程来执行(CallerRunsPolicy)。

    这四种策略是独立无关的,是对任务拒绝处理的四中表现形式。最简单的方式就是直接丢弃任务。但是却有两种方式,到底是该丢弃哪一个任务,比如可以丢弃当前将要加入队列的任务本身(DiscardPolicy)或者丢弃任务队列中最旧任务(DiscardOldestPolicy)。丢弃最旧任务也不是简单的丢弃最旧的任务,而是有一些额外的处理。除了丢弃任务还可以直接抛出一个异常(RejectedExecutionException),这是比较简单的方式。抛出异常的方式(AbortPolicy)尽管实现方式比较简单,但是由于抛出一个RuntimeException,因此会中断调用者的处理过程。除了抛出异常以外还可以不进入线程池执行,在这种方式(CallerRunsPolicy)中任务将有调用者线程去执行。

    示例 
    1,newCachedThreadPool 
    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程, 那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

    package io.ymq.thread.demo1;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 描述: 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。
     * 此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
     *
     * @author yanpenglei
     * @create 2017-10-12 11:13
     **/
    public class TestNewCachedThreadPool {
        public static void main(String[] args) {
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
            for (int i = 1; i <= 10; i++) {
                final int index = i;
                try {
                    Thread.sleep(index * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                cachedThreadPool.execute(new Runnable() {
    
                    @Override
                    public void run() {
                        String threadName = Thread.currentThread().getName();
                        System.out.println("执行:" + index + ",线程名称:" + threadName);
                    }
                });
            }
        }
    }

    响应: 
    执行:1,线程名称:pool-1-thread-1 
    执行:2,线程名称:pool-1-thread-1 
    执行:3,线程名称:pool-1-thread-1 
    执行:4,线程名称:pool-1-thread-1 
    执行:5,线程名称:pool-1-thread-1 
    执行:6,线程名称:pool-1-thread-1 
    执行:7,线程名称:pool-1-thread-1 
    执行:8,线程名称:pool-1-thread-1 
    执行:9,线程名称:pool-1-thread-1 
    执行:10,线程名称:pool-1-thread-1 
    2,newFixedThreadPool 
    描述:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。 
    线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

    package io.ymq.thread.demo2;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 描述:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
     * 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
     *
     * @author yanpenglei
     * @create 2017-10-12 11:30
     **/
    public class TestNewFixedThreadPool {
    
        public static void main(String[] args) {
    
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    
            for (int i = 1; i <= 10; i++) {
                final int index = i;
                fixedThreadPool.execute(new Runnable() {
    
                    @Override
                    public void run() {
                        try {
                            String threadName = Thread.currentThread().getName();
                            System.out.println("执行:" + index + ",线程名称:" + threadName);
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
    
                            e.printStackTrace();
                        }
                    }
                });
            }
    
        }
    }

    因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字,和线程名称。 
    响应: 
    执行:2,线程名称:pool-1-thread-2 
    执行:3,线程名称:pool-1-thread-3 
    执行:1,线程名称:pool-1-thread-1

    执行:4,线程名称:pool-1-thread-1 
    执行:6,线程名称:pool-1-thread-2 
    执行:5,线程名称:pool-1-thread-3

    执行:7,线程名称:pool-1-thread-1 
    执行:9,线程名称:pool-1-thread-3 
    执行:8,线程名称:pool-1-thread-2

    执行:10,线程名称:pool-1-thread-1

    3,newScheduledThreadPool 
    创建一个定长线程池,支持定时及周期性任务执行。延迟执行

    package io.ymq.thread.demo3;
    
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 描述:创建一个定长线程池,支持定时及周期性任务执行。延迟执行
     *
     * @author yanpenglei
     * @create 2017-10-12 11:53
     **/
    public class TestNewScheduledThreadPool {
    
        public static void main(String[] args) {
    
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    
            scheduledThreadPool.schedule(new Runnable() {
    
                @Override
                public void run() {
                    System.out.println("表示延迟3秒执行。");
                }
            }, 3, TimeUnit.SECONDS);
    
    
            scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
    
                @Override
                public void run() {
                    System.out.println("表示延迟1秒后每3秒执行一次。");
                }
            }, 1, 3, TimeUnit.SECONDS);
        }
    
    }

    表示延迟1秒后每3秒执行一次。 
    表示延迟3秒执行。 
    表示延迟1秒后每3秒执行一次。 
    表示延迟1秒后每3秒执行一次。 
    表示延迟1秒后每3秒执行一次。 
    表示延迟1秒后每3秒执行一次。 
    4,newSingleThreadExecutor 
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    package io.ymq.thread.demo4;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 描述:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
     *
     * @author yanpenglei
     * @create 2017-10-12 12:05
     **/
    public class TestNewSingleThreadExecutor {
    
        public static void main(String[] args) {
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
            for (int i = 1; i <= 10; i++) {
                final int index = i;
                singleThreadExecutor.execute(new Runnable() {
    
                    @Override
                    public void run() {
                        try {
                            String threadName = Thread.currentThread().getName();
                            System.out.println("执行:" + index + ",线程名称:" + threadName);
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }

    结果依次输出,相当于顺序执行各个任务。 
    响应: 
    执行:1,线程名称:pool-1-thread-1 
    执行:2,线程名称:pool-1-thread-1 
    执行:3,线程名称:pool-1-thread-1 
    执行:4,线程名称:pool-1-thread-1 
    执行:5,线程名称:pool-1-thread-1 
    执行:6,线程名称:pool-1-thread-1 
    执行:7,线程名称:pool-1-thread-1 
    执行:8,线程名称:pool-1-thread-1 
    执行:9,线程名称:pool-1-thread-1 
    执行:10,线程名称:pool-1-thread-1

    展开全文
  • Java中的四种线程池

    千次阅读 2022-05-17 09:56:36
    Java.util.concurrent中,提供了工具类Executors(调度器)对象来创建线程池,可创建的线程池四种: 1、CachedThreadPool - 可缓存线程池 特点:无限大,如果线程池中没有可用线程就会自动创建,有的话就自动利用...


    在Java.util.concurrent中,提供了工具类Executors(调度器)对象来创建线程池,可创建的线程池有四种:

    1、CachedThreadPool - 可缓存线程池

    特点:无限大,如果线程池中没有可用线程就会自动创建,有的话就自动利用起来。
    在这里插入图片描述

    2、FixedThreadPool - 定长线程池

    特点是:固定线程总数,空闲线程用于执行任务。如果线程都在执行任务后续任务则处于等待状态,在线程池中的线程执行任务后再执行后续任务。
    如果线程处于等待状态,备选的等待算法默认为FIFO(先进先出),还有LIFO(后进先出)

    在这里插入图片描述

    3、SingleThreadExecutor - 单线程池

    在这里插入图片描述

    4、ScheduledThreadPool - 调度线程池

    特点:可以根据设定的时间间隔执行任务。
    schedule()设定的时间间隔执行一次;
    scheduleAtFixedRate()设定的时间间隔重复执行。
    在这里插入图片描述

    展开全文
  • Java 四种线程池的用法分析

    万次阅读 多人点赞 2016-03-31 16:34:04
    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用,本文是基础篇。转载请标注原地址:http://blog.csdn.net/u011974987/article/details/51027795;1、new Thread的弊端执行一个异步任务你还只是如下...

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用,本文是基础篇。

    转载请标注原地址http://blog.csdn.net/u011974987/article/details/51027795


    1、new Thread的弊端

    执行一个异步任务你还只是如下new Thread吗?

    
    new Thread(new Runnable() {
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            }
        }
    ).start();
    

    那你就out太多了,new Thread的弊端如下:

    a. 每次new Thread新建对象性能差。
    b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
    c. 缺乏更多功能,如定时执行、定期执行、线程中断。

    相比new Thread,Java提供的四种线程池的好处在于:

    a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
    b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
    c. 提供定时执行、定期执行、单线程、并发数控制等功能。

    2、Java 线程池

    Java通过Executors提供四种线程池,分别为:

    • newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
    • newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    (1)newCachedThreadPool:

    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
        try {
            Thread.sleep(index * 1000);
        } 
            catch (InterruptedException e) {
                e.printStackTrace();
        }
    
    cachedThreadPool.execute(new Runnable() {
    
    @Override
    public void run() {
        System.out.println(index);
    }
    });
    }

    线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

    (2)newFixedThreadPool:

    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
        final int index = i;
    
        fixedThreadPool.execute(new Runnable() {
    
    @Override
    public void run() {
    try {
        System.out.println(index);
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
    }
    });
    }

    因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。

    定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。可参考PreloadDataCache。

    (3)newScheduledThreadPool:

    创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
     scheduledThreadPool.schedule(new Runnable() {
    
    @Override
    public void run() {
        System.out.println("delay 3 seconds");
    }
    }, 3, TimeUnit.SECONDS);
    

    表示延迟3秒执行。

    定期执行示例代码如下:

    
    scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
    
    @Override
    public void run() {
        System.out.println("delay 1 seconds, and excute every 3 seconds");
    }
    }, 1, 3, TimeUnit.SECONDS);
    

    表示延迟1秒后每3秒执行一次。

    ScheduledExecutorService比Timer更安全,功能更强大

    (4)newSingleThreadExecutor:

    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:

    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
    final int index = i;
    singleThreadExecutor.execute(new Runnable() {
    
    @Override
    public void run() {
        try {
            System.out.println(index);
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
            }
    }
        });
    }

    结果依次输出,相当于顺序执行各个任务。

    现行大多数GUI程序都是单线程的。Android中单线程可用于数据库操作,文件操作,应用批量安装,应用批量删除等不适合并发但可能IO阻塞性及影响UI线程响应的操作。

    线程池的作用:

    线程池作用就是限制系统中执行线程的数量。
    根 据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排 队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池 中有等待的工作线程,就可以开始运行了;否则进入等待队列。

    为什么要用线程池:

    1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

    2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

    比较重要的几个类:

    ExecutorService: 真正的线程池接口。

    ScheduledExecutorService: 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

    ThreadPoolExecutor: ExecutorService的默认实现。

    ScheduledThreadPoolExecutor: 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

    要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

    1.newSingleThreadExecutor

    创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    2.newFixedThreadPool

    创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

    3.newCachedThreadPool

    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

    那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

    4.newScheduledThreadPool

    创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

    实例代码

    一、固定大小的线程池,newFixedThreadPool:

    package app.executors;  
    
    import java.util.concurrent.Executors;  
    import java.util.concurrent.ExecutorService;  
    
    /** 
     * Java线程:线程池 
     *  
     * @author xiho
     */  
    public class Test {  
        public static void main(String[] args) {  
            // 创建一个可重用固定线程数的线程池  
            ExecutorService pool = Executors.newFixedThreadPool(2);  
            // 创建线程  
            Thread t1 = new MyThread();  
            Thread t2 = new MyThread();  
            Thread t3 = new MyThread();  
            Thread t4 = new MyThread();  
            Thread t5 = new MyThread();  
            // 将线程放入池中进行执行  
            pool.execute(t1);  
            pool.execute(t2);  
            pool.execute(t3);  
            pool.execute(t4);  
            pool.execute(t5);  
            // 关闭线程池  
            pool.shutdown();  
        }  
    }  
    
    class MyThread extends Thread {  
        @Override  
        public void run() {  
            System.out.println(Thread.currentThread().getName() + "正在执行。。。");  
        }  
    }  

    输出结果:

    pool-1-thread-1正在执行。。。  
    pool-1-thread-3正在执行。。。  
    pool-1-thread-4正在执行。。。  
    pool-1-thread-2正在执行。。。  
    pool-1-thread-5正在执行。。。  

    改变ExecutorService pool = Executors.newFixedThreadPool(5)中的参数:ExecutorService pool = Executors.newFixedThreadPool(2),输出结果是:

    pool-1-thread-1正在执行。。。  
    pool-1-thread-1正在执行。。。  
    pool-1-thread-2正在执行。。。  
    pool-1-thread-1正在执行。。。  
    pool-1-thread-2正在执行。。。  

    从以上结果可以看出,newFixedThreadPool的参数指定了可以运行的线程的最大数目,超过这个数目的线程加进去以后,不会运行。其次,加入线程池的线程属于托管状态,线程的运行不受加入顺序的影响。

    二、单任务线程池,newSingleThreadExecutor:

    仅仅是把上述代码中的ExecutorService pool = Executors.newFixedThreadPool(2)改为ExecutorService pool = Executors.newSingleThreadExecutor();
    输出结果:

    pool-1-thread-1正在执行。。。  
    pool-1-thread-1正在执行。。。  
    pool-1-thread-1正在执行。。。  
    pool-1-thread-1正在执行。。。  
    pool-1-thread-1正在执行。。。  

    可以看出,每次调用execute方法,其实最后都是调用了thread-1的run方法。

    三、可变尺寸的线程池,newCachedThreadPool:

    与上面的类似,只是改动下pool的创建方式:ExecutorService pool = Executors.newCachedThreadPool();

    输出结果:

    pool-1-thread-1正在执行。。。  
    pool-1-thread-2正在执行。。。  
    pool-1-thread-4正在执行。。。  
    pool-1-thread-3正在执行。。。  
    pool-1-thread-5正在执行。。。  

    这种方式的特点是:可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

    四、延迟连接池,newScheduledThreadPool:

    public class TestScheduledThreadPoolExecutor {
    
        public static void main(String[] args) {
    
            ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
    
            exec.scheduleAtFixedRate(new Runnable() {//每隔一段时间就触发异常
    
                          @Override
    
                          publicvoid run() {
    
                               //throw new RuntimeException();
    
                               System.out.println("================");
    
                          }
    
                      }, 1000, 5000, TimeUnit.MILLISECONDS);
    
            exec.scheduleAtFixedRate(new Runnable() {//每隔一段时间打印系统时间,证明两者是互不影响的
    
                          @Override
    
                          publicvoid run() {
    
                               System.out.println(System.nanoTime());
    
                          }
    
                      }, 1000, 2000, TimeUnit.MILLISECONDS);
    
        }
    
    }

    输出结果:

    ================
    
    8384644549516
    
    8386643829034
    
    8388643830710
    
    ================
    
    8390643851383
    
    8392643879319
    
    8400643939383
    展开全文
  • java 四种线程池的简介

    千次阅读 2016-08-14 13:58:03
    前瞻四种线程池内部构造都是来自同一个方法: 下面分别介绍一下各个参数的含义: corePoolSize: 线程池中所保存的核心线程数。线程池初始化启动之后,默认是空的,只有当任务来临之时,才会建立线程 处理请求。...
  • Java中的四种线程池

    2020-09-20 21:36:22
    Java.util.concurrent中,提供了工具类Executors(调度器)对象来创建线程池,可创建的线程池四种: 1、CachedThreadPool - 可缓存线程池 特点:无限大,如果线程池中没有可用线程就会自动创建,有的话就自动利用...
  • java 四种 线程池

    千次阅读 2016-10-14 09:19:04
    java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池,可...
  • Java四种线程池的使用

    千次阅读 2019-05-30 22:42:58
    一、普通new Thread线程 new Thread(()->{}).start(); 缺点: 每次new Thread新建对象性能差,线程缺乏统一管理,可能无限制新建线程,相互之间...Java通过Executors提供四种线程池: 1)、newCachedThr...
  • Java四种线程池介绍

    万次阅读 2017-12-03 10:47:15
    Java四种线程池
  • 之前笔记有记录java线程池的拒绝策略,回顾一下线程池的处理任务的优先级: 先考虑corePoolSize、任务队列(缓冲队列)workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。 即: ...
  • Java中使用线程池,可以用ThreadPoolExecutor的构造函数直接创建出线程池实例,在Executors类中,为我们提供了常用线程池的创建方法。​接下来我们就来了解常用的四种: 首先,看一下这种线程池的创建方法...
  • Java四种线程池和工作队列

    千次阅读 2017-12-02 19:31:24
    构造ThreadPoolExecutor对象时,需要配置该对象的核心线程池大小和最大线程池大小: 当目前执行线程的总数小于核心线程大小时,所有新加入的任务,都在新线程中处理 当目前执行线程的总数大于或等于核心线程时,...
  • 主要给大家介绍了关于Java四种线程池的使用方法,四种线程池分别包括FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor,文中给出了详细的示例代码供大家参考,需要的朋友们下面来...
  • Java四种线程池之newCachedThreadPool 文章目录Java四种线程池之newCachedThreadPool一、概念二、使用步骤1.引入库2.读入数据总结 一、概念 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收...
  • 今天小编就为大家分享一篇关于Java四种常用线程池的详细介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • java中常见的四种线程池的区别

    千次阅读 2018-12-18 16:02:09
    查看下java.util.concurrent.Executors源码,可以发现有四种创建线程池的方法,分别为:newCacbedThreadPool、newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor,其实这四种创建线程池的方法都...
  • 下面小编就为大家带来一篇详谈Java种线程池类型介绍及使用方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了Java ExecutorService四种线程池使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Java四种线程池及new Thread的弊端

    千次阅读 2018-10-29 23:43:27
    提供的四种线程池的好处在于:  1)重用存在的线程,减少对象创建、消亡的开销,提升性能。   2)可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。   3)提供...
  • Java线程池四种创建方式

    千次阅读 2021-12-03 15:17:30
    Java线程池四种创建方式 Java使用Thread类来表示线程,所有的线程都是Thread类或者是他的子类。Java有四种方式来创建线程。 (1)继承Thread类创建线程 (2)实现Runnable接口创建线程 (3)使用Callable和Future...
  • 下面我们说说通过Executors创建的四种线程池的弊端:因为使用Executors创建线程池不会传入拒绝策略这个参数而使用默认值所以我们常常忽略这一参数. 让我们再看看Executors提供的那几个工厂方法。
  • Java四种线程池使用方法

    千次阅读 2018-06-06 15:44:18
    1-2 相比new Thread,Java提供的四种线程池的好处在于: a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。 b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。 c. 提供...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 335,989
精华内容 134,395
关键字:

java的四种线程池