线程和线程池

作者: 小编 | 来源:发表于2017-03-17 12:46 被阅读74次

线程

在java中实现线程的方式:

  1. 继承Thread类
  2. 实现Runable接口。

main方法其实也是一个线程。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。

  • 对比
    实现Runnable接口比继承Thread类所具有的优势:
    1):适合多个相同的程序代码的线程去处理同一个资源
    2):可以避免java中的单继承的限制
    3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

  • yield()方法

    Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
    yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

  • join()方法
    保证当前线程停止执行,直到该线程所加入的线程完成为止,当前线程方可继续执行。然而,如果它加入的线程没有存活,则当前线程不需要停止。

AsyncTask

  • 内部由两个线程池和一个Handler组成。
    1. SerialExecutor:用于任务的排队,一次执行一个。
    2. ThreadPoolExecutor:用于真正的执行任务。
    3. InternalHandler:用于发送结果数据从子线程到主线程。
  • 执行流程:
    构造方法中实例化WorkerRunnable和FutureTask对象。
    WorkerRunnable将Params参数封装,并将自己封装在FutureTask中,一旦FutureTask执行run方法时,会去调用WorkRunnable的call方法并返回Result值。call方法中就执行了AsyncTask的doInBackground方法。并调用postResult方法将结果发送出去。
mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //noinspection unchecked
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        return postResult(result);
    }
};

  private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
  }

那么FutureTask是什么时候执行的?
FutureTask充当了Runnable的作用,交给SerialExecutor的execute方法执行。FutureTask是一个并发类,可以中途取消的用于异步计算的类。

SerialExecutor的execute方法首先把FutureTask插入到mTasks任务队列中,如果没有活动的任务,则执行下一个。当一个任务执行完成,会继续调用scheduleNext方法执行下一个,直到所有任务都被执行。

THREAD_POOL_EXECUTOR.execute(mActive);才是真正执行任务的方法。使用的是ThreadPoolExecutor线程池。

private static class SerialExecutor implements Executor {
  final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
  Runnable mActive;

  public synchronized void execute(final Runnable r) {
      //将FutureTask插入到mTasks任务队列中
      mTasks.offer(new Runnable() {
          public void run() {
              try {
                  // 执行FutureTask的run方法,进而执行call方法
                  r.run();
              } finally {
                  // 串行执行下一个任务
                  scheduleNext();
              }
          }
      });
      //没有正在活动的任务,执行下一个AsyncTask。
      if (mActive == null) {
          scheduleNext();
      }
  }

  protected synchronized void scheduleNext() {
      if ((mActive = mTasks.poll()) != null) {
          THREAD_POOL_EXECUTOR.execute(mActive);
      }
  }
}
  • 注意:
  1. AsyncTask对象必须在主线程中创建
  2. execute必须在主线程中调用
  3. 一个AsyncTask只能调用一次execute方法,

HandlerThread

继承了Thread类,本质还是线程。但是可以直接使用Handler的Thread。在run方法中通过Looper.prepare创建消息队列,并开启消息循环。使得可以再此线程中创建Handler。

由于loop开启了无限循环,因此可以通过quit或者quitSafely方法终止线程执行。

同时,它还解决了一个Looper与Handler的同步问题。可以保证根据当前线程的Looper创建Handler时,Looper对象的获取不为空。
参考《深入理解Android 卷I》159页

    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

典型应用场景就是在IntentService中。

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

IntentService

一个可以处理异步请求的Service.服务开启后,工作线程会依次处理每个Intent,任务执行完毕后会自动关闭。

相对于线程而言,IntentService更适合执行一些高优先级的后台任务。

    @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);
    }

线程池

  • 优点
  1. 重用线程池中的线程,避免因为线程的创建和销毁带来的性能开销。
  2. 有效控制线程的最大并发数,避免因大量的线程之间互相抢占系统资源而导致的阻塞 。
  3. 能够对线程进行简单的管理,并提供定时执行和执行循环间隔执行等功能
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {  }
  • 变量

    • corePoolSize: 核心线程数
    • maximumPoolSize: 最大线程数
    • workQueue:任务队列,提交的Runnable对象存储在这里。
    • keepAliveTime: 非核心线程存活时间
    • unit:keepAliveTime的时间单位。
    • threadFactory:为线程池提供新线程的工厂。
    • handler:异常处理策略。
  • 规则

  1. 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
  2. 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
  3. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
  4. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

参阅:
Java多线程学习

相关文章

  • Springboot | 线程池的学习,多线程池配置示例

    一、线程和进程,线程的生命周期二、单线程和多线程三、线程池的概念四、线程池的使用五、多线程池配置示例 一、线程和进...

  • 线程池查漏补缺

    tomcat线程池和jdk线程池区别 概述 线程池是什么,为什么要线程池 jdk有哪些线程池和原理 第三方中间件的...

  • 线程池概述

    为什么要使用线程池? 线程池核心参数 线程池的几种拒绝策略 execute()和submit()的区别 线程池工作...

  • java线程池

    线程VS线程池 普通线程使用 创建线程池 执行任务 执行完毕,释放线程对象 线程池 创建线程池 拿线程池线程去执行...

  • 第九章 线程池

    线程池的实现原理和使用建议。 当提交一个新任务到线程池时,线程池的处理流程如下。1)线程池判断核心线程池里的线程是...

  • 我们的线程被饿死了

    我们的线程被饿死了 我们在构建线程池的时候可以构建单个线程的线程池和多个线程的线程池。 那么线程池使用不当可不可能...

  • Android线程池的使用

    一、线程与线程池,为什么要使用线程池 1、Android中的线程 在Android中有主线程和子线程的区分。主线程...

  • Java线程池的使用

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

  • Android面试Java基础篇(二)

    (一)问:Java线程池的实现原理和使用 线程池即存放和管理线程的一个池子 (1)复用线程池中的线程,避免因为线程...

  • java----线程池

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

网友评论

    本文标题:线程和线程池

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