美文网首页
线程池基本使用

线程池基本使用

作者: 者文_ | 来源:发表于2019-08-21 15:03 被阅读0次

1. 线程池使用

使用new Thread()创建线程存在的问题:

  • 频繁创建线程,执行完后又被回收,导致频繁GC
  • 多线程缺乏统一管理,各线程之间互相竞争,降低程序运行效率
  • 无法有效控制线程的执行、取消等。

使用线程池的优点

  • 重用线程池中的线程,避免线程的创建和销毁带来的性能开销
  • 有效控制线程池的最大并发数,避免大量线程之间因胡抢占资源导致阻塞现象
  • 对线程进行简单管理,并提供定时执行,指定间隔循环执行等

Android中的线程池源于Java中的Executor,其本身是一个接口,真正的实现类是ThreadPoolExecutor

1.1 ThreadPoolExecutor

ThreadPoolExecutor有四个重载的构造方法,介绍以下参数最多的构造器如下:

public ThreadPoolExecutor(int corePoolSize, 
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue, 
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 

参数含义

  • corePoolSize: 线程池中核心线程的数量,默认情况会在线程池中一直存活。可以通过allowCoreThreadTimeOutkeepAliveTime设置核心线程闲置超时终止。
  • maximumPoolSize: 线程池中最大线程数量,活动线程数达到这个数,后续新任务会阻塞
  • keepAliveTime: 非核心线程的超时时长
  • unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等(TimeUnit.MILLISECONDS\TimeUnit.SECONDS........)
  • workQueue 线程池中的任务队列, 通过线程池execute方法提交的Runnable对象会存储在这个参数中
  • threadFactory 为线程池提供创建新线程的功能, 是一个接口,只有一个方法:Thread newThead(Runnable r)
  • handler 拒绝策略 当线程无法执行新任务时,默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException(拒绝策略有:CallerRunsPolicyAbortPolicyDiscardPolicyDiscardOldestPolicy)

运行规则

  • 运行线程,线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行
  • 运行线程,核心线程数已满且workQueue未满,新线程放入workQueue中等待执行
  • 运行线程,核心线程已满、workQueue已满且未超过非核心线程数开启一个非核心线程来执行任务
  • 运行线程,三者都满拒绝执行该任务

示例:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        poolExecutor = new ThreadPoolExecutor(3, 5,
                1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(128));
}
 
    public void btnClick(View view) {
        for (int i = 0; i < 30; i++) {
            final int finalI = i;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    SystemClock.sleep(2000);
                    Log.d("google_lenve_fb", "run: " + finalI);
                }
            };
            poolExecutor.execute(runnable);
        }
    }

线程池中线程相关参数该怎么配置呢?可以参考AsyncTask。其源码如下:

public abstract class AsyncTask<Params, Progress, Result> {
    private static final String LOG_TAG = "AsyncTask";
 
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;
 
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
 
        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };
 
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
 
    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
        ....
        ....
}

从上述代码我们可以知道:

  • 核心线程数为手机CPU数 + 1(cpu数量获取方式Runtime.getRuntime().availableProcessors()
  • 线程池线程数为手机CPU数*2 + 1
  • 线程队列大小:128

如果要使用线程池,可以参考这个再根据实际配置。

1.2 线程池分类

线程池可以分为四类:FixedThreadPoolCachedThreadPoolScheduledThreadPool、以及SingleThreadExecutor

  • FixedThreadPool

    是一个核心线程数量固定的线程池。通过Executors的newFixedThreadPool创建。

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    

    其源码为:

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

    FixedThreadPool中没有非核心线程,所有的线程都是核心线程,它们不会被回收,除非线程池被关闭了。核心线程没有超时机制。

  • CachedThreadPool

    通过Executor的newCachedThreadPool,是一种线程数量不定的线程池,没有核心线程,最大线程数Integer.MAX_VALUE,有线程超时机制,超时时间60秒。使用SynchronousQueue作为线程队列,适合执行大量的耗时较少任务。

    源码如下:

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

    通过Executor的newScheduledThreadPool创建,核心线程数固定,非核心线程数没有限制。一旦非核心线程闲置被立即回收。适合执行定时任务和具有固定周期的重复任务。

    public static ScheduledExecutorService new ScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
        DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
        new DelayedWorkQueue());
    }
    
  • SingleThreadPool

    通过Executor的newSingleThreadPool创建。只有一个核心线程

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

1.3 线程池常用功能

1.shutDown() 关闭线程池,不影响已经提交的任务

2.shutDownNow() 关闭线程池,并尝试去终止正在执行的线程

3.allowCoreThreadTimeOut(boolean value) 允许核心线程闲置超时时被回收

4.submit 一般情况下我们使用execute来提交任务,但是有时候可能也会用到submit,使用submit的好处是submit有返回值

相关文章

  • 1203-AsyncTask详解一:线程池的基本设置

    AsyncTask的内部使用线程池处理并发,要了解它是怎样使用线程池的,那要先了解线程池的基本设置 线程池的基本参...

  • 线程池

    1.为什么要使用线程池?2.线程池的基本原理是什么?3.怎么学习线程池?线程池使用了池化技术。池化技术用于我们常见...

  • python3 线程池和异常处理

    引用 线程池的基本使用as_completedwaitmap 线程池的异常处理 进程池用法 引用 Python中已...

  • HystrixCommand实现线程隔离

    Hystrix的基本使用 线程隔离原理 存在全局的线程池线程池缓存,根据threadPoolKey来区分不同的线程...

  • Java线程池如何保证线程池的核心线程存活不被销毁?execut

    线程池介绍 基本元素 线程池是一个创建使用线程并能保存使用过的线程以达到服用的对象,他使用workQueue当作阻...

  • 线程池基本使用

    1. 线程池使用 使用new Thread()创建线程存在的问题: 频繁创建线程,执行完后又被回收,导致频繁GC ...

  • 线程池解析第一章-源码解析

    线程池解析第一章-源码解析线程池解析第二章-线程池源码问题总结 线程池基本介绍 为什么要使用线程池 对于系统和服务...

  • Java线程池的使用

    线程类型: 固定线程 cached线程 定时线程 固定线程池使用 cache线程池使用 定时调度线程池使用

  • Java并发系列:线程池ThreadPoolExecutor参数

    上篇回顾 上篇我们讲了,线程池的创建、提交任务到线程池、关闭线程池。基本使用我们已经没有多大问题了。上节我们创建线...

  • java----线程池

    什么是线程池 为什么要使用线程池 线程池的处理逻辑 如何使用线程池 如何合理配置线程池的大小 结语 什么是线程池 ...

网友评论

      本文标题:线程池基本使用

      本文链接:https://www.haomeiwen.com/subject/dqzbsctx.html