Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池

简介: 前言:由于最近在做SDK的功能,需要设计线程池。看了很多资料不知道从何开始着手,突然发现了AsyncTask有对线程池的封装,so,就拿它开刀,本文将从AsyncTask的基本用法,到简单的封装,再到任务队列分析,最后自定义线程池。
前言:由于最近在做SDK的功能,需要设计线程池。看了很多资料不知道从何开始着手,突然发现了AsyncTask有对线程池的封装,so,就拿它开刀,本文将从AsyncTask的基本用法,到简单的封装,再到任务队列分析,最后自定义线程池。

 

1、概念

    Android 中的异步任务常用的一种方式是:Handler + Thread 组合来实现的。Thread 负责子线程的耗时操作,Handler 负责线程间的通信,用的最多的当属子线程和主线程通信。

    Android 为了简化操作,提供了 AsyncTask 类来实现异步任务,并且轻松实现子线程和主线程间的通信。

2、AsyncTask的简单封装

    三个参数代表的含义

  • Params:第一个参数是启动任务传进来的参数;
  • Progress:第二个参数是用来显示进度条的参数;
  • Result:第三个参数是后台执行后返回的参数的类型。
package com.app;

import android.os.AsyncTask;

/**
 * Created by ${zyj} on 2016/8/2.
 */
public class MyTask<T> extends AsyncTask<T , Integer , T> {

    private TaskListener taskListener ;

    public MyTask(){

    }

    //执行预处理,它运行于UI线程,可以为后台任务做一些准备工作,比如绘制一个进度条控件。
    @Override
    protected void onPreExecute() {
        if ( taskListener != null ){
            taskListener.start();
        }
    }

    //运行于UI线程,可以对后台任务的结果做出处理,结果就是doInBackground(Params...)的返回值。
    @Override
    protected void onPostExecute(T t) {
        if ( taskListener != null ){
            taskListener.result( t );
        }
    }

    /**
     * 更新子线程进度,运行于UI线程
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {;
        if ( taskListener != null ){
            taskListener.update( values[0] );
        }
    }

    //运行与后台线程
    @Override
    protected T doInBackground(T... ts) {
        if ( taskListener != null ){
            return (T) taskListener.doInBackground( ts[0] ) ;
        }
        return null;
    }

    public MyTask setTaskListener(TaskListener taskListener ){
        this.taskListener = taskListener ;
        return this ;
    }

    /**
     * 更新进度
     * @param progress
     */
    public void updateProgress( int progress ){
        publishProgress( progress );
    }

    public interface TaskListener<T>{
        void start() ;
        void update( int progress  ) ;
        T doInBackground( T t );
        void result( T t );
    }

    /**
     * 取消一个正在执行的任务
     */
    public void cancle(){
        if ( !isCancelled() ){
            cancel( true ) ;
        }
    }
}

 

3、简单的异步任务使用

package com.app;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import wifi.app.wei.com.myapplication.R;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new MyTask<String>().setTaskListener(new MyTask.TaskListener() {
            @Override
            public void start() {
                Log.d( "task--" ,  "start 开始了, 运行在主线程" ) ;
            }

            @Override
            public void update(int progress) {

            }

            @Override
            public Object doInBackground(Object o) {
                Log.d( "task--" ,  "doInBackground , 运行在子线程" ) ;
                return null;
            }

            @Override
            public void result(Object o) {
                Log.d( "task--" ,  "result , 运行在主线程" ) ;
            }
        }).execute( "" ) ;

    }
}

  

 4、带有进度更新的异步任务使用

package com.app;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

import wifi.app.wei.com.myapplication.R;

public class MainActivity extends AppCompatActivity {

    private TextView textView ;
    private MyTask myTask ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById( R.id.tv1 );

        myTask =  new MyTask<String>().setTaskListener(new MyTask.TaskListener() {
            @Override
            public void start() {
                Log.d( "task--" ,  "start 开始了, 运行在主线程" ) ;
                textView.setText( "任务开始了" );
            }

            @Override
            public void update(int progress) {
                textView.setText( "进度" + progress );
            }

            @Override
            public Object doInBackground(Object o) {
                Log.d( "task--" ,  "doInBackground , 运行在子线程" ) ;
                for ( int i = 0 ; i < 100 ; i++ ){
                    try {
                        Thread.sleep( 100 ) ;
                        myTask.updateProgress( i ) ;  //每隔100毫秒,更新一下进度
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return "结束了";
            }

            @Override
            public void result(Object o) {
                Log.d( "task--" ,  "result , 运行在主线程" ) ;
                textView.setText( "" + o );
            }
        }) ;

        //开始执行任务
        myTask.execute( "" ) ;

    }
}

  执行效果图

 

5、AsyncTask 任务执行应该注意的细节

  (1)、如果异步任务需要联网,则需要添加联网权限

             <uses-permission android:name="android.permission.INTERNET"/>

      (2)、AsyncTask实例必须在UI线程中创建,execute(Params…)方法必须在UI线程中调用。不用手动调用onPreExecute()。

     (3)、一个任务只能被执行一次

 

6、如何取消任务

     可以调用 myTask.cancle() ;  

     但是这个方法并没有真正的结束任务,只是设置了一个标志位,把当前线程中断了。

     取消任务实践

package com.app;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import wifi.app.wei.com.myapplication.R;

public class MainActivity extends AppCompatActivity {

    private TextView textView ;
    private MyTask myTask ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById( R.id.tv1 );
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //取消任务
                myTask.cancle();
            }
        });

        myTask =  new MyTask<String>().setTaskListener(new MyTask.TaskListener() {
            @Override
            public void start() {
                Log.d( "task--" ,  "start 开始了, 运行在主线程" ) ;
                textView.setText( "任务开始了" );
            }

            @Override
            public void update(int progress) {
                textView.setText( "进度" + progress );
            }

            @Override
            public Object doInBackground(Object o) {
                Log.d( "task--" ,  "doInBackground , 运行在子线程" ) ;
                for ( int i = 0 ; i < 100 ; i++ ){
                    try {
                        Thread.sleep( 100 ) ;
                        myTask.updateProgress( i ) ;  //每隔100毫秒,更新一下进度
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return "结束了";
            }

            @Override
            public void result(Object o) {
                Log.d( "task--" ,  "result , 运行在主线程" ) ;
                textView.setText( "" + o );
            }
        }) ;

        //开始执行任务
        myTask.execute( "" ) ;

    }
}

  当点击textView时,调用了 myTask.cancle() ;方法后,Android studio 控制台抛出了异常

 

通过这里我们发现,AsyncTask 虽然提供了cancle( true )  方法来停止任务,但是这个方法只是中断了这个线程,但是并不能真正意思上的停止任务,这也是很多人说 AsyncTask 的弊端。极容易造成内存溢出的。

 

几种结束任务的间接实现方式:

1、判断标志位的办法:

我们要知道在java的线程中,没有办法停止一个正在运行中的线程。在Android的AsyncTask中也是一样的。如果必须要停止一个线程,我们可以采用这个线程中设置一个标志位,然后在线程run方法或AsyncTask的doInBackground方法中的关键步骤判断这个标志位以决定是否继续执行。然后在需要终止此线程的地方改变这个标志位以达到停止线程的目的。

2、合理的利用Exception

从外部调用AsyncTask的cancel方法并不能停止一个已经启动的AsyncTask。这个cancel方法的作用与线程的interrupt方法相似,调用了一个线程的interrupt方法之后线程仍然运行,但是如果该线程的run方法里面调用过sleep的或者wait方法后,处于sleep或wait状态,则sleep和wait立即结束并且抛出InterruptedException异常。AsyncTask的cancel方法也一样,如果在这个Task的doInBackground方法中调用了sleep或wait方法,当在UI线程中调用了这个Task实例的cancel方法之后,sleep或wait立即结束并且抛出InterruptedException异常,但是如果捕获该异常的代码后面还有其他代码,则这些代码还会继续执行。

3、可以在UI上做手脚

如果用户在后台线程正获取内容时做出了取消的行为,我们可以根据用户的这种行为在UI上立即做出反馈,此时,即使线程完成了数据的Loading,我们也不让数据显示出来,算是一种投机取巧的办法吧。

7、AsyncTask 串行处理任务 和 并行处理任务

     在上面的代码演示中,执行任务用的都是 myTask.execute() , 这个默认是串行执行任务的。比如同一时刻有两个任务要处理,AsyncTask 会先执行第一个任务,等第一个任务执行结束,然后才会执行第二个任务。

     在AsyncTask中还有一个并行处理任务的方法:executeOnExecutor( Executor exe , Params... params ) 。 

 

     下面是串行执行任务execute()的源码

     

 

  通过看源码,发现其实串行执行任务也是调用了并行的方法 executeOnExecutor () , 只不过启用了一个默认的 sDefaultExecutor (sDefaultExecutor 是一个串行的线程池)。

  有串行线程池,那么势必就有一个并行线程池 , 在AsyncTask里面源码里面定义了一个并行线程池: THREAD_POOL_EXECUTOR 。

  

       可以看到并行 THREAD_POOL_EXECUTOR 是通过 new ThreadPoolExecutor() 来创建的

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

参数说明:

corePoolSize: 线程池维护线程的最少数量 
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略

 

我们知道,受限于硬件、内存和性能,我们不可能无限制的创建任意数量的线程,因为每一台机器允许的最大线程是一个有界值。也就是说ThreadPoolExecutor管理的线程数量是有界的。线程池就是用这些有限个数的线程,去执行提交的任务。然而对于多用户、高并发的应用来说,提交的任务数量非常巨大,一定会比允许的最大线程数多很多。为了解决这个问题,必须要引入排队机制,或者是在内存中,或者是在硬盘等容量很大的存储介质中。J.U.C提供的ThreadPoolExecutor只支持任务在内存中排队,通过BlockingQueue暂存还没有来得及执行的任务。

任务的管理是一件比较容易的事,复杂的是线程的管理,这会涉及线程数量、等待/唤醒、同步/锁、线程创建和死亡等问题。ThreadPoolExecutor与线程相关的几个成员变量是:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize,它们共同负责线程的创建和销毁。

corePoolSize:

线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,所以没有任务需要执行的时候,线程池的大小不一定是corePoolSize。

maximumPoolSize:

线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。

poolSize:

线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize。

keepAliveTime

当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。

     

了解了各个参数的含义之后,我们来看看 AsyncTask 中默认的并行线程队列 THREAD_POOL_EXECUTOR 各项的数值

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);
  • corePoolSize 为cup数加 1  ;       
  • maximumPoolSize 为cup数的2倍加1
  • 存活时间为1秒
  • 任务缓存队列为 LinkedBlockingQueue

   

     小测试:我手上的手机是联想 k50-t5 ,  在设置里面看到处理器为 8 核1.7GHZ , 运行 Runtime.getRuntime().availableProcessors(); 方法得到的值为:8

     另外我们也可以总结出:

  •  同一台手机上THREAD_POOL_EXECUTOR 的 corePoolSize 和 maximumPoolSize 的值是固定的。
  •  在不同的手机上,THREAD_POOL_EXECUTOR 的 corePoolSize 和 maximumPoolSize 的值是不同的。 这种动态设置的方法值得我们学习,在不同的设备上所使用的策略是不同的。但是也是方式也是有弊端的,任务并发数是由cpu的限定的,不可人为的修改。

 

总结:

        //开始执行 串行任务
        myTask.execute( "" ) ;
        或者
        myTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR , "" ) ;

        //开始执行 并行任务
        myTask.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "" ) ;

 

 8、自定义线程池

    上一部分我们已经明白了AsyncTask 的默认并行线程池 THREAD_POOL_EXECUTOR 是通过 new ThreadPoolExecutor() 来创建的 , 那么我们也可以自己定义一个线程池。

        首先来看 ThreadPoolExecutor 的构造函数

        

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

    
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                threadFactory, defaultHandler);
    }

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

    
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

  通过看构造方法,发现 corePoolSize 、maximunPoolSize 、keepAliveTime 、unit 、workQueue 是必须要写的。

  分析最后一个构造

if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
            throw new IllegalArgumentException();

  corePoolSize :最小值 0 

     maximunPoolSize :最小值 1

      corePoolSize 必须小于或者等于 maximunPoolSize 

 

    主要来看 workQueue , 这个是就是线程队列了。

    下面是AsyncTask并行线程池 THREAD_POOL_EXECUTOR 里面所使用的线程队列,128 代表线程队列的长度

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

     下面给出一个完整的例子:

 

        //创建缓冲队列 队列长度:100
        BlockingQueue<Runnable> sPoolWorkQueue =
                new LinkedBlockingQueue<Runnable>(100);
        
        //创建线程池 核心线程:5个   最大线程:10个   线程空闲存活时间:1秒
        Executor executor = new ThreadPoolExecutor( 5 , 10  , 1  , TimeUnit.SECONDS ,
                sPoolWorkQueue ) ;

        //添加任务到缓冲队列
        myTask1.executeOnExecutor( executor , "" ) ;

  

  

线程创建规则

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。
当一个任务通过execute(Runnable)方法欲添加到线程池时:
 1、  如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
 2、  如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
 3、  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
 4、  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
 5、  当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

 

线程池按以下行为执行任务

  1. 当线程数小于核心线程数时,创建线程。
  2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  3. 当线程数大于等于核心线程数,且任务队列已满
    1. 若线程数小于最大线程数,创建线程
    2. 若线程数等于最大线程数,抛出异常,拒绝任务

 

任务队列执行的逻辑:

    FIFO  先进先出 

 

9、自定义线程池的简便方法

      在第8节我们详解了如何自定义线程池,讲解了 ThreadPoolExecutor 构造方法的每个参数的用法,但是如果自定义线程池都要写那么多参数,岂不是很麻烦。

     幸运的是,系统的 java.util.concurrent 包下面 Executors 类提供了很多简单的方法,供我们使用,这对苦逼的码农来说,是很好的福音。

      Executors 里面的方法有 

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

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

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                        0L, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<Runnable>()));
    }
    
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
                (parallelism,
                        ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                        null, true);
    }

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

    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
                (new ScheduledThreadPoolExecutor(1, threadFactory));
    }
    
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
    }
    
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(),
                threadFactory);
    }
    
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
    
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

  猛一看,方法那么多,其实把方法单独领出来

    public static ExecutorService newFixedThreadPool(int nThreads)
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
        
    public static ExecutorService newSingleThreadExecutor()
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

    public static ExecutorService newWorkStealingPool()    
    public static ExecutorService newWorkStealingPool(int parallelism)
        
    public static ScheduledExecutorService newSingleThreadScheduledExecutor()
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) 

    public static ExecutorService newCachedThreadPool()
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    public static ScheduledExecutorService newScheduledThreadPool ( int corePoolSize, ThreadFactory threadFactory)

  这样一归类,就清晰很多了,就12个方法,然后每2个方法又可以归为一组,也就是6组。

     9.1、 newFixedThreadPool(int nThreads)  创建固定大小的线程池

public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

      这两个函数用于创建一个最大线程数目固定的线程池,该线程池用一个共享的无界队列来存储提交的任务。参数nThreads指定线程池的最大线程数,参数threadFactory是线程工厂类,主要用于自定义线程池中创建新线程时的行为。需要说明的是,创建线程池时,如果线程池没有接收到任何任务,则线程池中不会创建新线程,在线程池中线程数目少于最大线程数时,每来一个新任务就创建一个新线程,当线程数达到最大线程数时,不再创建新线程,新来的任务存储在队列中,之后线程数目不再变化!

创建一个线程数量固定大小,任务队列无限大的线程池。当队列中需要的线程数超出定义的线程的时候,所有任务将在队列中排队,等待空闲线程执行。     

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

  

    9.2、 newSingleThreadExecutor  创建只有一个线程的线程池,其实就相当于串行线程池         

              线程池中的任务使用无界队列存储,第二个函数可以指定threadFactory,自定义创建线程时的行为。

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

       

     9.3、newWorkStealingPool

              这个方法用于创建ForkJoin框架中用到的ForkJoinPool线程池,第一个函数中的参数用于指定并行数,第二个函数没有参数,它默认使用当前机器可用的CPU个数作为并行数。

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

  

     9.4 、 newSingleThreadScheduledExecutor

           该方法用于创建只有一个线程的线程池,并且该线程定时周期性地执行给定的任务

           需要注意的是: 线程在周期性地执行任务时如果遇到Exception,则以后将不再周期性地执行任务。

    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

  

     9.5、newCachedThreadPool

      方法用于创建线程数数目可以随着实际情况自动调节的线程池,也有两种类型的函数签名:

public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

     这种线程池的最大线程数只受到操作系统可以创建的最大线程数数目限制,当线程池有很多任务需要处理时,会不断地创建新线程,当任务处理完毕之后,如果某个线程空闲时间大于60s,则该线程将会被销毁。因为这种线程池能够自动调节线程数量,所以比较适合执行大量的短期的小任务。

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

     

     9.6、newScheduledThreadPool 

            创建一个大小无限的线程池,线程池中得线程能够周期性地执行给定的任务,有两种函数签名:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory)

   

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

  

 

 

总结:

1、本篇文章的demo例子已上传至github: https://github.com/zyj1609wz/AsyncTaskDemo

2、本人微信公众账号:zhaoyanjun125  ,   欢迎关注

    

 

相关文章
|
27天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
49 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
20天前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
51 5
|
29天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
47 10
|
27天前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
29天前
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。
|
2月前
|
存储 Java 开发者
HashMap线程安全问题大揭秘:ConcurrentHashMap、自定义同步,一文让你彻底解锁!
【8月更文挑战第24天】HashMap是Java集合框架中不可或缺的一部分,以其高效的键值对存储和快速访问能力广受开发者欢迎。本文深入探讨了HashMap在JDK 1.8后的底层结构——数组+链表+红黑树混合模式,这种设计既利用了数组的快速定位优势,又通过链表和红黑树有效解决了哈希冲突问题。数组作为基石,每个元素包含一个Node节点,通过next指针形成链表;当链表长度过长时,采用红黑树进行优化,显著提升性能。此外,还介绍了HashMap的扩容机制,确保即使在数据量增大时也能保持高效运作。通过示例代码展示如何使用HashMap进行基本操作,帮助理解其实现原理及应用场景。
40 1
|
2月前
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享安卓与iOS开发中的线程管理比较
【8月更文挑战第30天】本文将探讨网络安全与信息安全的重要性,并分享关于网络安全漏洞、加密技术和安全意识的知识。我们将了解常见的网络攻击类型和防御策略,以及如何通过加密技术和提高安全意识来保护个人和组织的信息安全。
|
2月前
|
Java 测试技术 Android开发
Android项目架构设计问题之构造一个Android中的线程池如何解决
Android项目架构设计问题之构造一个Android中的线程池如何解决
24 0
|
2月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
70 1
|
11天前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口