Android下Java多线程及线程池机制总结笔记

主线程和子线程

           主线程是指进程中拥有的线程,在Java中默认情况下一个进程只有一个线程,这个线程就是主线程。主线程主要处理界面交互相关的逻辑。因为用户随时会和界面发生交互,因此主线程在任何时候都必须有较高的响应速度,否则就会产生一种页面卡顿的感觉。为了保持较高的响应速度,这就要求主线程中不能执行耗时的任务,就需要子线程来完成这些任务。子线程也叫工作线程,除了主线程以外的线程都是子线程。

              Android沿用了Java的线程模型,其中的线程也分为主线程和子线程,其中UI线程就是主线程。主线程的作用是运行四大组件以及处理他们和用户的交互,而子线程的作用则是耗时任务,比如网络请求,I/O操作等。从Android 3.0开始要求网络访问必须在子线程中进行,否则网络访问会失败并会抛出NetworkOnMainThreadException异常。这样做是为了避免主线程由于耗时操作从而出现ANR现象。


Android中的线程形态

              在Android中我们开启子线程最简单方法就是new Thread,实现run方法,执行耗时操作。除了Thread之外,Android系统还封装了Thread具有特殊表现形式的线程。针对不用使用场景和功能分为:AsyncTask,HandlerThread,IntentService。这三者底层实现的都是线程,同时在使用上也各有优缺点。

      

AsyncTask

  这部分请参考:AsyncTask异步任务机制源码分析和总结笔记


HandlerThread


              HandlerThread继承了Thread,它是一种可以使用Handler的Thread,它的实现很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际使用中就允许在HandlerThread中创建Handler了。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

HandlerThread的实现来看,他和普通的Thread有显著的不同之处,普通的Thread主要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式;来通知HandlerThread执行一个具体的任务。由于HandlerThread的run方法是一个无限循环,因此当明确不需要再使用HandlerThread时,可以通过他的quit或者quitSafely方法来终止线程的执行,这是一个良好的编程习惯。

使用范例:

HandlerThread  mHandlerThread = new HandlerThread("Test", 5);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
}

private Runnable mRunnable = new Runnable() {

@Override
public void run() {
    while (mRunning) {
     Log.d("MainActivity", "test HandlerThread...");
     try {
     Thread.sleep(200);
      } catch (Exception e) {
     e.printStackTrace();
     }
   }

  }
};

IntentService

Intent是一种特殊的Service,他继承了Service并且他是一个抽象类,因此必须创建他的子类才能使用IntentService。IntentService可用于执行后台耗时的任务,当任务执行完成后它会自动停止,同事由于IntentService是服务的原因,导致它的优先级比单纯的线程要高得多,所以IntentService比较适合执行一些高优先级的后台任务,以内他优先级高不容易被杀死。实际上,IntentService封装了Handler和HandlerThread。在onCreate方法中。

@Override
public void onCreate() {
    // TODO: It would be nice to have an option to hold a partial wakelock
    // during processing, and to have a static startService(Context, Intent)
    // method that would launch the service & hand off a wakelock.

    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

当IntentService被第一次启动时,它的onCreate方法会被调用,onCreate方法会创建一个HandlerThread,然后使用它的Looper来构造一个Handler对象mServiceHanlder,这样通过mServiceHandler发送的消息最终都会在HandlerThread中执行。每次启动IntentService,它的onStartCommand方法就会被调用一次,IntentService在onStartCommand中处理每个后台任务的Intent。

/**
 * You should not override this method for your IntentService. Instead,
 * override {@link #onHandleIntent}, which the system calls when the IntentService
 * receives a start request.
 * @see android.app.Service#onStartCommand
 */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

在onStartCommand方法中调用了onStart方法,在onStart方法中可以了看出,IntentService仅仅是通过mServiceHandler发送了一个消息,这个消息会在Handlerthread中被处理。mServiceHandler收到消息后,会将Intent对象传递给onHandlerIntent方法中去处理。注意这个Intent对象的内容和外界的startService(intent)中intent的内容是完全一致的。通过这个Intent对象即可解析出外界启动IntentService是所传递的参数,通过这些参数就可以区分具体的后台任务,这样在onHandlerIntent方法中就可以对不同的后台任务做出来了。

当onhandlerIntent方法执行结束后,Intent会通过stopSelf(int startId)方法来尝试停止服务,而这个时候之所以采用stopSelf(int startId)而不是stopSelf()来停止服务,那是因为stopSelf()会立刻停止服务,而这个时候可能还有其他的消息未处理,stopSelf(int startId)则会等待所有的消息都处理完毕后才停止服务。

一般来说,stopSelf(int startId)在尝试停止服务之前会判断最近启动的服务次数是否和startId相等,如果相等就立刻停止服务,不相等则不停止服务。这个策略可以从AMS的stopServiceToken方法的实现找到依据。


Android中的线程池

线程池在Android开发中使用的优势特别明显,主要可以概括为三点:

 

(1)   重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销

(2)   能够有效控制线程池的最大并发数,避免大量的线程之间因相互抢占系统资源而导致的阻塞现象。

(3)   能够对线程进行简单的管理,并提供定时执行以及制定间隔循环执行等功能

 

Android中线程池的概念源于Java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor提供了一系列参数来配置线程池,下面介绍ThreadPoolExecutor的构造方法中的各个参数的含义,这些参数将直接影响到线程池的功能特性,下面是ThreadPoolExecutor的一个比较常用的构造方法。

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters and default thread factory.
 *
 * @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.
 * @param handler the handler to use when execution is blocked
 *        because the thread bounds and queue capacities are reached
 * @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}
 *         or {@code handler} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

corePoolSize

线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即时他们处于闲置状态。如果将ThreadPoolExecutor的aollwThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔有keepAliveTime所指定,当等待时间超过keepAliveTime所指定的时长后,核心线程就会被终止。

 

maximumPoolSize

   线程池所能容纳的最大线程数,当活动线程数达到这个最大值后,后续的新任务将会被阻塞。


keepAliveTime

    非核心线程闲置是的超时时长,超过这个时长,非核心线程就会被回收。当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程。


unit

   用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒),以及TimeUnit.MINUTES(分钟)等

 

workQueue

线程池中的任务队列,通过线程池的executor方法提交的Runnable对象会存储在这个参数中。

 

threadFactory

线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,他只是一个方法:Thread newThread(Runnable r)

 

ThreadPoolExecutor执行任务是大致遵循如下规则:

 

(1)  如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。

(2)  如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。

(3)  如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。

(4)  如果步骤3中线程数量已达到线程池规定的最大值,那么就拒绝执行此任务,

 

下面我们以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);

从上面的代码可以看出,Asynctask对ThreadPoolExecutor线程池的配置规格

 

1,     核心线程数等于CPU核心数+1;

2,     线程池的最大线程数等于CPU核心数的2倍+1;

3,     核心线程无超时机制,非核心线程在限制时的超时时间为1秒

4,     任务队列的容量为128;

 

线程池的分类

1, FixedThreadPool

线程数量固定的线程池,当线程处于空闲状态时,他们并不会被回收,除非线程池被关闭了。FixedThreadPool只有核心线程并且这些核心线程不会被回收,意味着它能更快速的响应外界的请求。

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

2, CachedThreadPool

一种线程数量不定的线程池,它只有非核心线程,并且最大的线程数为Integer.MAX_VALUE。

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

3, ScheduledThreadPool

核心线程数固定,而非核心线程数没有限制。并且当非核心线程闲置时会被立即回收。这类线程池主要用于执行定时任务和具有固定周期的重复任务。

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

 /**
     * 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());
    }

4, SingleThreadPool

线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在于统一所有外界任务到一个线程中,使得在这些任务之间不需要处理线程同步的问题(顺序执行)。

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





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值