• new Thread到线程池使用


    该篇文章目录

      1.使用线程池原因

      2.Thread

      3.线程池

      4.线程池工厂提供的四种线程池

      5.总结

    一.使用线程池原因

    参考:http://blog.csdn.net/mine_song/article/details/70948223

    https://blog.csdn.net/seu_calvin/article/details/52415337 

    参考的文章

    刚开始学习异步任务,当然是用Thread+handler进行异步任务,但是有时bug多和难以维护,我们引入线程池。

    二.Thread

    • Thread的使用
    new Thread(new Runnable(){
        @override
         public void run(){
          
         }
    }).start();
    new Thread(Runnable runnable).start();
    new Thread(FutureTask<V>{Callable callable}).start(); 

       处理时间=T1(线程创建时间)+T2(线程处理任务时间)+T3(线程销毁时间)

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

    三.线程池

      1.狭义上的线程池

        线程池是一种多线程处理的方式,处理过程中将任务加入到任务队列,然后创建线程进行执行,线程池中所有线程都是后台线程,并且默认是优先级下运。

        如果当前是线程空闲状态,一有任务添加则让空闲线程执行。

        如果当前所有线程都处于繁忙状态,则让任务挂起,直到直到线程池数量不再是最大值。

      2.广义上的线程池

        多线程技术主要解决处理器单元内多个线程执行的问题。可以明显减少处理器单元闲置的时间,增加处理器单元的吞吐量。

      3.线程池的优势

      • 通过对线程进行缓存,减少对象创建、销毁的时间,性能佳。
      • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
      • 提供定时执行、定期执行、单线程、并发数控制等功能。

      4.重要的几个类:

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

    ExecutorService

    真正的线程池接口。

    ScheduledExecutorService

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

    ThreadPoolExecutor

    ExecutorService的默认实现。

    ScheduledThreadPoolExecutor

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

      5.使用

        1.执行:

        Future future = executor.submit(Runnable runnable);

        Future future = executor.submit(Callable callable);,返回的future有意义

        Future future = executor.execute(Runnable runnable);返回的future没有意义

        2.销毁:

        executor.shutdown();

      •   shutdown() 

        •   will just tell the executor service that it can't accept new tasks, but the already submitted tasks continue to run

      •   shutdownNow() 

        •   will do the same AND will try to cancel the already submitted tasks by interrupting the relevant threads. Note that if your tasks ignore the interruption, shutdownNow will behave exactly the same way as shutdown.

      5.自定义线程池参数:

    /**
         * Creates a new {@code ThreadPoolExecutor} with the given initial
         * parameters and default thread factory and rejected execution handler.
         * It may be more convenient to use one of the {@link Executors} factory
         * methods instead of this general purpose constructor.
         *
         * @param corePoolSize the number of threads to keep in the pool, even
         *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
         * @param maximumPoolSize the maximum number of threads to allow in the
         *        pool
         * @param keepAliveTime when the number of threads is greater than
         *        the core, this is the maximum time that excess idle threads
         *        will wait for new tasks before terminating.
         * @param unit the time unit for the {@code keepAliveTime} argument
         * @param workQueue the queue to use for holding tasks before they are
         *        executed.  This queue will hold only the {@code Runnable}
         *        tasks submitted by the {@code execute} method.
         * @throws IllegalArgumentException if one of the following holds:<br>
         *         {@code corePoolSize < 0}<br>
         *         {@code keepAliveTime < 0}<br>
         *         {@code maximumPoolSize <= 0}<br>
         *         {@code maximumPoolSize < corePoolSize}
         * @throws NullPointerException if {@code workQueue} is null
         */
        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 Executors.defaultThreadFactory(), defaultHandler);
        }

        参数说明如下:

      • int corePoolSize: 核心线程数。
      • int maximumPoolSize: 最大线程数,当任务进来时可以扩充的线程最大值,当大于了这个值就会根据丢弃处理机制来处理
      • long keepAliveTime: 作用于非核心线程。当线程数大于corePoolSize时,多余的空闲线程的最大存活时间,类似于HTTP中的Keep-alive
      • TimeUnit unit:

          时间单位,一般用秒

      • BlockingQueue<Runnable> workQueue:

          //缓冲任务队列,线程池的execute方法会将Runnable对象存储起来.

          //工作队列,先进先出,可以看出并不像Picasso那样设置优先队列。详细链接:阻塞队列详解

      • ThreadFactory threadFactory: 
        //线程工厂接口,只有一个new Thread(Runnable r)方法,可为线程池创建新线程

    (1)当currentSize<corePoolSize时,没什么好说的,直接启动一个核心线程并执行任务。

    (2)当currentSize>=corePoolSize、并且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。

    (3)当workQueue已满,但是currentSize<maximumPoolSize时,会立即开启一个非核心线程来执行任务。

    (4)当currentSize>=corePoolSize、workQueue已满、并且currentSize>maximumPoolSize时,调用handler默认抛出RejectExecutionExpection异常。

    四.线程池工厂提供的四种线程池

    1.定义

    • newFixedThreadPool

         创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。阻塞队列为LinkedBlockingQueue

    • newCachedThreadPool

        创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。阻塞队列为SynchronousQueue

    • newScheduledThreadPool

        创建一个定长线程池,支持定时及周期性任务执行。阻塞队列为DelayedWorkQueue

    • newSingleThreadExecutor

        创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。阻塞队列为LinkedBlockingQueue

    2.四种线程池构造函数

      四种线程池的构造器就是在ThreadPoolExecutor的基础上进行封装的。

    public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }
    (一堆人排队上公厕)

    1)从配置参数来看,FixThreadPool只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。

    
    

    (2)【前方高能,笔者脑洞】FixThreadPool其实就像一堆人排队上公厕一样,可以无数多人排队,但是厕所位置就那么多,而且没人上时,厕所也不会被拆迁,哈哈o(∩_∩)o ,很形象吧。

    
    

    (3)由于线程不会回收,FixThreadPool会更快地响应外界请求,这也很容易理解,就好像有人突然想上厕所,公厕不是现用现建的。

     

    public
    static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
    (一堆人去一家很大的咖啡馆喝咖啡)

    (1)CachedThreadPool只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程(60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务。

    
    

    (2)任务队列SynchronousQueue相当于一个空集合,导致任何任务都会被立即执行。

    
    

    (3)【前方高能,笔者脑洞】CachedThreadPool就像是一堆人去一个很大的咖啡馆喝咖啡,里面服务员也很多,随时去,随时都可以喝到咖啡。但是为了响应国家的“光盘行动”,一个人喝剩下的咖啡会被保留60秒,供新来的客人使用,哈哈哈哈哈,好恶心啊。如果你运气好,没有剩下的咖啡,你会得到一杯新咖啡。但是以前客人剩下的咖啡超过60秒,就变质了,会被服务员回收掉。

    
    

    (4)比较适合执行大量的耗时较少的任务。喝咖啡人挺多的,喝的时间也不长。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
        }
    (4个里面唯一一个有延迟执行和周期重复执行的线程池)

    1)核心线程数固定,非核心线程(闲着没活干会被立即回收)数没有限制。

    
    

    (2)从上面代码也可以看出,ScheduledThreadPool主要用于执行定时任务以及有固定周期的重复任务。

     
    /**
         * Creates a new {@code ScheduledThreadPoolExecutor} with the
         * given core pool size.
         *
         * @param corePoolSize the number of threads to keep in the pool, even
         *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
         * @throws IllegalArgumentException if {@code corePoolSize < 0}
         */
        public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE,
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                  new DelayedWorkQueue());
        }
    public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }

    (公厕里只有一个坑位)

    3.四种线程池的使用

      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秒执行一次。

      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线程响应的操作。

     不过最近有专门的Rxjava框架应对Thread+Handler的多线程管理问题。

  • 相关阅读:
    小任务之使用SVG画柱状图~
    webpack.config.js配置遇到Error: Cannot find module '@babel/core'问题
    今日笔记
    querySelector与getElementBy等的区别
    JavaScript实现邮箱后缀提示功能
    JavaScript中按键事件的e.keyCode || e.which || e.charCode
    跳转页面&回到上一页
    echarts.js 做图表的插件
    局域网内电脑远程访问共享
    同引擎mysql数据库转导快
  • 原文地址:https://www.cnblogs.com/could-deng/p/6734552.html
Copyright © 2020-2023  润新知