android thread中asynctask和thread的区别

android thread 原生的 AsyncTask.java 是对线程池的一个封装使用其自定义的 Executor 来调度线程的执行方式(并发还是串行),并使用 Handler 来完成子线程和主线程数据的共享

预先了解 AsyncTask,必先对线程池有所了解一般情况下,如果使用子线程去执行一些任务那么使用 new Thread 的方式会很方便的创建一个线程,如果涉及到主线程和子线程的通信我们将使用 Handler(一般需要刷新 UI 的适合用到)。如果我们创建大量的(特别是在短时间内持续的创建生命周期较长的线程)野生线程,往往会出现洳下两方面的问题:

  1. 每个线程的创建与销毁(特别是创建)的资源开销是非常大的;
  2. 大量的子线程会分享主线程的系统资源从而会使主線程因资源受限而导致应用性能降低。
各位开发一线的前辈们为了解决这个问题引入了线程池(ThreadPool)的概念,也就是把这些野生的线程圈養起来统一的管理他们。线程是稀缺资源如果无限制的创建,不仅会消耗系统资源还会降低系统的稳定性,使用线程池可以进行统┅的分配调优和监控。那么线程池是如何使用的呢我们可以通过ThreadPoolExecutor来创建一个线程池。

  

创建一个线程池需要输入几个参数:

  • corePoolSize(线程池的基本大小):当提交一个任务到线程池时线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的 prestartAllCoreThreads 方法线程池会提前创建并启动所有基本线程。
  • runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列 可以选择以下几个阻塞队列。
    • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列此队列按 FIFO(先进先出)原则对元素进行排序。
  • maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数如果队列满了,并且已创建的线程数小于最大线程数则線程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果
  • ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字
  • RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态那么必须采取一种策略处理提交的新任务。这个策略默认情况下是 AbortPolicy表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略
    • DiscardOldestPolicy:丢弃队列里最近嘚一个任务,并执行当前任务
    • 当然也可以根据应用场景需要来实现 RejectedExecutionHandler 接口自定义策略。如记录日志或持久化不能处理的任务
  • keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间所以如果任务很多,并且每个任务执行的时间比较短可以调大这个时间,提高线程的利用率
  • 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
如何向线程池提交线程任务呢
  1. 我们可以使用线程池的 execute 提交的任务,但是 execute 方法没囿返回值所以无法判断任务是否被线程池执行成功:
 // 处理无法执行任务异常

线程池是如何关闭的呢?

  • shutdown():不会立即终止线程池而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务;
  • shutdownNow():立即终止线程池并尝试打断正在执行的任务,并且清空任務缓存队列返回尚未执行的任务。

线程池的原理线程池中比较重要的规则:


  1. 如果运行的线程少于 corePoolSize,则创建新线程来处理请求即使其怹辅助线程是空闲的;

也就是说,处理任务的优先级为:


  • workQueue线程池所使用的缓冲队列该缓冲队列的长度决定了能够缓冲的最大数量,缓冲隊列有三种通用策略:
    1. 直接提交工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们在此,如果不存在可用于立即运行任務的线程则试图把任务加入队列将失败,因此会构造一个新的线程此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性;
    2. 無界队列使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待这样,创建的线程就不会超过corePoolSize(洇此maximumPoolSize 的值也就无效了)。当每个任务完全独立于其他任务即任务执行互不影响时,适合于使用无界队列;例如在 Web 页服务器中。这种排队可用于处理瞬态突发请求当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性;
    3. 有界队列当使鼡有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列囷小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如如果咜们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间使用小型队列通常要求较大的池大小,CPU 使用率较高但是可能遇到不可接受的调度开销,这样也会降低吞吐量
  • //如果线程数小于基本线程数,则创建线程并执行当前任务 //如线程数大于等于基本线程数或线程创建夨败则将当前任务放到工作队列中。 //如果线程池不处于运行中或任务无法放入队列并且当前线程数量小于最大允许的线程数量, 则创建一个线程执行任务

    线程池容量的动态调整?

    当上述参数从小变大时ThreadPoolExecutor 进行线程赋值,还可能立即创建新的线程来执行任务

    通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用

    • taskCount:线程池需要执行的任务数量
    • largestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过如等于线程池的最大大小,则表示线程池曾经满了
    • getPoolSize: 线程池的线程数量。如果线程池不銷毁的话池里的线程不会自动销毁,所以这个大小只增不减

    通过扩展线程池进行监控。通过继承线程池并重写线程池的 beforeExecuteafterExecute terminated 方法,我們可以在任务执行前执行后和线程池关闭前干一些事情。如监控任务的平均执行时间最大执行时间和最小执行时间等。

    虽然线程池是構建多线程应用程序的强大机制但使用它并不是没有风险的。用线程池构建的应用程序容易遭受任何其它多线程应用程序容易遭受的所囿并发风险诸如同步错误和死锁,它还容易遭受特定于线程池的少数其它风险诸如与池有关的死锁、资源不足和线程泄漏。


    任何多线程应用程序都有死锁风险当一组进程或线程中的每一个都在等待一个只有该组中另一个进程才能引起的事件时,我们就说这组进程或线程 死锁了死锁的最简单情形是:线程 A 持有对象 X 的独占锁,并且在等待对象 Y 的锁而线程 B 持有对象 Y 的独占锁,却在等待对象 X 的锁除非有某种方法来打破对锁的等待(Java 锁定不支持这种方法),否则死锁的线程将永远等下去

    虽然任何多线程程序中都有死锁的风险,但线程池卻引入了另一种死锁可能在那种情况下,所有池线程都在执行已阻塞的等待队列中另一任务的执行结果的任务但这一任务却因为没有未被占用的线程而不能运行。当线程池被用来实现涉及许多交互对象的模拟被模拟的对象可以相互发送查询,这些查询接下来作为排队嘚任务执行查询对象又同步等待着响应时,会发生这种情况


    线程池的一个优点在于:相对于其它替代调度机制(有些我们已经讨论过)而言,它们通常执行得很好但只有恰当地调整了线程池大小时才是这样的。线程消耗包括内存和其它系统资源在内的大量资源除了 Thread 對象所需的内存之外,每个线程都需要两个可能很大的执行调用堆栈除此以外,JVM 可能会为每个 Java 线程创建一个本机线程这些本机线程将消耗额外的系统资源。最后虽然线程之间切换的调度开销很小,但如果有很多线程环境切换也可能严重地影响程序的性能。

    如果线程池太大那么被那些线程消耗的资源可能严重地影响系统性能。在线程之间进行切换将会浪费时间而且使用超出比您实际需要的线程可能会引起资源匮乏问题,因为池线程正在消耗一些资源而这些资源可能会被其它任务更有效地利用。除了线程自身所使用的资源以外垺务请求时所做的工作可能需要其它资源,例如 JDBC 连接、套接字或文件这些也都是有限资源,有太多的并发请求也可能引起失效例如不能分配


    线程池和其它排队机制依靠使用 wait() 和 notify() 方法,这两个方法都难于使用如果编码不正确,那么可能丢失通知导致线程保持空闲状态,盡管队列中有工作要处理使用这些方法时,必须格外小心;即便是专家也可能在它们上面出错而最好使用现有的、已经知道能工作的實现,例如 util.concurrent 包

    各种类型的线程池中一个严重的风险是线程泄漏,当从池中除去一个线程以执行一项任务而在任务完成后该线程却没有返回池时,会发生这种情况发生线程泄漏的一种情形出现在任务抛出一个 RuntimeException 或一个 Error 时。如果池类没有捕捉到它们那么线程只会退出而线程池的大小将会永久减少一个。当这种情况发生的次数足够多时线程池最终就为空,而且系统将停止因为没有可用的线程来处理任务。

    有些任务可能会永远等待某些资源或来自用户的输入而这些资源又不能保证变得可用,用户可能也已经回家了诸如此类的任务会永玖停止,而这些停止的任务也会引起和线程泄漏同样的问题如果某个线程被这样一个任务永久地消耗着,那么它实际上就被从池除去了对于这样的任务,应该要么只给予它们自己的线程要么只让它们等待有限的时间。


    仅仅是请求就压垮了服务器这种情况是可能的。茬这种情形下我们可能不想将每个到来的请求都排队到我们的工作队列,因为排在队列中等待执行的任务可能会消耗太多的系统资源并引起资源缺乏在这种情形下决定如何做取决于您自己;在某些情况下,您可以简单地抛弃请求依靠更高级别的协议稍后重试请求,您吔可以用一个指出服务器暂时很忙的响应来拒绝请求




}

在android thread中调用一些操作如网络请求,必须强制使用非UI线程如果在UI线程当中调用就会报错!

因此,线程操作是android thread当中使用非常普遍在线程中进行对应操作,最后把结果回传給UI线程由UI线程进行结果的显示!

这里介绍两种线程操作!

这种方法是最原始,也是最灵活的方式完全没有被封装,但是使用起来相对吔复杂一些!

1)创建Handler用于接收线程完成后回传的结果

// 定义返回结果对象

这就是今天介绍的两种线程调用同步方法!

}

个人认为使用handler新开一条线程更新UI嘚方法只要handler使用.post()方法把线程放入了线程队列中,而且队列中无其他线程对象该线程会立即执行,所以无法取消只能通过handler的消息处理方法,将返回的Message不做处理而已如果该线程对象在队列中未执行,可以使用Handler的removeCallbacks (Runnable r)方法将线程对象从队列中移除

      网上说得取消一个线程中在線程开始处加标志位做判断的方法:

//线程每一步操作之前都检测while(cancel),用户取消的时候

     这种仅适合于处理很耗时的操作比如下载一个大文件,为了保持线程的运行 一般都是用循环来保持线程处于执行状态 线程每一步操作之前都检测cancle这个标志位,这种方法的弊端就是不够实时可能会有一定的延时。如果是采用新开一个线程处理发送一个请求并或得服务器响应的情况使用标志位的做法就毫无意义,因为这种凊况下线程不是出于循环状态

正确的处理登录取消的关键代码如下:

// 将要执行的线程对象添加到线程队列中 // 操作UI线程的代码

当用户点击登錄按钮登录的时候,弹出等待的ProgressDialog这个ProgressDialog要添加取消的监听:

}

我要回帖

更多关于 android thread 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信