day3 线程

线程基础知识

线程状态

线程状态

线程可以有如下6中状态

  • New(新创建)
  • Runable(可运行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed waiting(计时等待)
  • Terminated(被终止)
    可调用用getState()方法获取当前状态

新创建线程

比如new Thread(r),该线程还没有开始运行,意味着它的状态为new。

可运行线程

一旦调用start方法,线程处于runable状态,一个可运行的线程可能正在运行也可能没有运行,这取决与操作系统给线程提供运行的时间。一旦一个线程开始运行,它不必始终都在运行,事实上,运行过程中,它可能会被中断,而中断的目的就是让其他线程获得运行机会,线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片来执行任务,当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会。

被阻塞线程和等待线程

当线程被阻塞或等待线程时,它暂时不活动。它不运行任何代码且消耗最少的资源,知道线程调度器激活它。细节取决于它是怎样达到非活动状态的。

  • 当一个线程试图获取一个内部的对象锁,而该锁被其它线程所有,则该线程进入阻塞状态,当其它线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程将变成非阻塞状态。
  • 当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。
    • 调用Object.wait方法或Thread.join方法
    • 等待java.util.concurrent库中的Lock或Condition时
  • 有几个方法有一个超时参数。调用它们导致线程进入计时等待状态。这一状态将一直保持到超时期满或者接收到适当的通知。带有超时期参数的方法有Thread.sleep和Object.wait,Thread.join,Lock.tryLock以及Condition.await的计时版。

被终止的线程

线程因如下两个原因之一而被终止:

  • 因为run方法正常退出而死亡
  • 因为一个没有捕获的异常终止了run方法而意外死亡

可以调用线程的stop方法杀死线程,该方法抛出一个ThreadDeath错误对象,由此杀死对象,但是,stop方法已经过时,不要再自己的代码中调用这个方法。

线程属性

包括:线程优先级,守护线程,线程组以及处理未捕获异常的处理器

线程优先级

每一个线程有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以用setPriority方法提高或降低任何一个线程的优先级,范围是MIN_PRIORITY(1)与MAX_PRIORITY(10)之间的任何值。NORM_PRIORITY被定义为5.
每当线程调度器有机会选择新线程时,它首先会选择具有较高优先级的线程。

守护线程

可以通过t.setDaemon(true)将线程转换为守护线程,守护线程的唯一用途是为其它线程提供服务。计时线程就是一个例子,它定时地发送“计时器滴答”信号给其它线程或清空过时的高速缓存项的线程。当只剩下守护线程时,虚拟机就退出了,由于如果只剩下守护线程,就没必要继续运行程序了。

未捕获异常处理器

线程的run方法不能抛出任何被检测的异常,但是,不被检测的异常会导致线程终止,在这种情况下,线程就死亡了。
不需要任何catch字句就可以来处理可被传播的异常,相反,就在线程死亡之前,异常被传递到一个用于未捕获异常的处理器,该处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类,这个接口只要一个方法:void uncaughtException(Thread t,Throwable e),可以用setUncaughtExceptionHandler为任何线程安装一个处理器,也可以用Thread类的setDefaultUncaughtExceptionHandler()为所有线程安装一个默认的处理器。
如果不安装默认的处理器,默认的处理器为空

同步

在大多数多线程应用中,两个或两个以上的线程需要共享对同一数据的存取。根据各线程访问数据的次序,可能会产生讹误的对象,这种情况成为竞争条件。如果并发的每个线程对共享数据的操作源于块不是原子操作,即它实际上编译后是多个虚拟机指令,比如说增值命令是由几条指令组成的,那么执行它们的线程可以在任何一条指令点上被中断。

锁对象

有两种机制防止代码块并发访问的干扰。

  • Java语言提供一个synchronized关键字,并且Java SE 5.0引入了ReentrantLock类。synchronized关键字自动提供一个锁以及相关的“条件”,对于大多数需要显示锁的情况很便利。
  • java.util.concurrent框架
myLock.lock();//a ReentrantLock object
try{
  critical section
}
finally{
    myLock.unlock();//make sure the lock is unlocked even if an exception is thrown
 }

这一结构确保任何时刻只有一个线程进入临界区,一旦一个线程*了对象,其它任何线程都无法通过lock语言。当其它线程调用lock时,他们被阻塞,知道第一个线程释放锁对象。

android 的消息机制

android的消息机制主要是指handler的运行机制,handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue是消息队列,内部储存了一组消息,以队列的形式对外提供插入和删除的工作,MessageQueue只是个消息的存储单元,它不能去处理消息,而Looper就可以以无限循环的形式去查找是否有新消息,如果有的话就处理,没有的话就一直等待着。Handler创建的时候会采用当前线程的Looper来构造消息循环系统。
具体介绍,这儿有一篇很好的文章,http://www.jianshu.com/p/d88e1af5100f

什么姿势开线程?

new thread()

new Thread(new Runnable() {
            @Override
            public void run() {

            }
        }).start();

优点:这种方式简单粗暴,是Android系统中开线程最简单的方式,也只能应用于最简单的场景,简单却伴随着不少的隐患。
缺点

  • 仅仅是启动了一个新的线程,没有任务的概念,不能做状态的管理
  • Runnable作为匿名内部类还持有了外部类的引用,先线程退出之前,改引用会一直存在,阻碍外部类对象被GC回收,在一段时间内造成内存泄漏。
  • 没有线程切换的接口,要传递处理结果到UI线程的话,需要些额外的线程切换代码。

ThreadPoolExecutor 线程池

优点:

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

AsyncTask

public class MyAsyncTask extends AsyncTask {

       @Override
       protected Object doInBackground(Object[] params) {
           return null;
       }

       @Override
       protected void onPreExecute() {
           super.onPreExecute();
       }

       @Override
       protected void onPostExecute(Object o) {
           super.onPostExecute(o);
       }
   }

优点:

  • 几处回调里可以有机会去中断任务,在任务状态的管理上较之Thread()方式更为灵活,AsyncTask的cancel()方法并不会终止任务的执行,开发者需要自己去检查cancel的状态值来决定是否中止任务。

  • 线程优先级为background,对UI线程的执行影响极小。
    缺点:

  • AsyncTask也有隐式的持有外部类对象引用的问题,需要特别注意防止出现意外的内存泄漏。

  • AsyncTask由于在不同的系统版本上串行与并行的执行行为不一致,被不少开发者所诟病,这确实是硬伤,绝大部分的多线程场景都需要明确任务是串行还是并行。

HandlerThread

HandlerThread将Handler,Thread,Looper,MessageQueue几个概念相结合。Handler是线程对外的接口,所有新的message或者runnable都通过handler post到工作线程。Looper在MessageQueue取到新的任务就切换到工作线程去执行。不同的post方法可以让我们对任务做精细的控制,什么时候执行,执行的顺序都可以控制。HandlerThread最大的优势在于引入MessageQueue概念,可以进行多任务队列管理。

特点:

  • HandlerThread背后只有一个线程,任务是串行执行的。
  • HandlerThread产生的线程会一直存活,Looper会在该线程中持续的检查MessageQueue。
  • 较之Thread,AsyncTask需要写更多的代码,但在实用性,灵活性,安全性上都有更好的表现。

IntentService

它相当于是Service和HandlerThread的结合体,就是在Service里面开了个线程,只是在所有的Message处理完毕之后,工作线程会自动结束,然后Service也会销毁。

Rxjava

观察者与被观察者模式,两个角色可以在不同的线程执行。

上一篇:java byte处理


下一篇:java基础之引用