android线程管理二(Thread)

来源:互联网 发布:epud阅读器 mac 编辑:程序博客网 时间:2024/06/05 15:34

前言

   本篇主要从源码上分析一下Thread,转载请注明出处:小石头的博客 http://blog.csdn.net/lu1024188315/article/details/74518599

一 结构关系

publicclassThreadimplementsRunnable{
  ......}
很显然Thread继承了Runnable。
Runnable源码如下:
public interface Runnable {    public abstract void run();}
Runnable很简单,它是一个接口,只用一个方法run();

二 构造函数

Thread#构造方法:
public Thread() {        init(null, null, "Thread-" + nextThreadNum(), 0);}public Thread(Runnable target) {        init(null, target, "Thread-" + nextThreadNum(), 0);}public Thread(ThreadGroup group, Runnable target) {        init(group, target, "Thread-" + nextThreadNum(), 0);}......
  说明,这只是部分构造方法,从这些构造方法中可以看出都是调用了函数init。
Thread#init
/**     * 很明显这个方法是用来初始化线程的     * @param g 线程组 你创建的线程最终会被添加到这个线程组中     * @param target  Runnable类型 用于回调     * @param name 即将创建的线程的名字     * @param stackSize 即将创建的线程的栈的大小,如果是0,表明忽略此参数     */    private void init(ThreadGroup g, Runnable target, String name, long stackSize) {        //调用native层的currentThread函数获取当前环境所在的线程,例如在Activity中,你获取的将是UI线程        Thread parent = currentThread();        if (g == null) {           //线程parent所属的线程组            g = parent.getThreadGroup();        }        //函数1 后台线程数加1         g.addUnstarted();        //保存线程组        this.group = g;        //保存  Runnable类型 用于回调        this.target = target;        //parent获取线程优先权1-10        this.priority = parent.getPriority();        //是否是后台线程        this.daemon = parent.isDaemon();        //设置线程的名字        setName(name);        //函数2         init2(parent);        /* Stash the specified stack size in case the VM cares */        this.stackSize = stackSize;        //创建线程Id,线程的唯一标识        tid = nextThreadID();    }

1  addUnstarted

Thread#addUnstarted
void addUnstarted() {        synchronized(this) {            if (destroyed) {                throw new IllegalThreadStateException();            }            nUnstartedThreads++;        }}
注释已经说的很清楚了:
/**
* Increments the count of unstarted threads in the thread group.
* Unstarted threads are not added to the thread group so that they
* can be collected if they are never started, but they must be
* counted so that daemon thread groups with unstarted threads in
* them are not destroyed.
*/
大致意思就是:把thread group 中的即将启动的线程数加一,这样可以在该线程没有启动的情况,避免被销毁。

int

Thread#int2
private void init2(Thread parent) {        //获取线程parent的类加载器 ClassLoader类型        this.contextClassLoader = parent.getContextClassLoader();        this.inheritedAccessControlContext = AccessController.getContext();        if (parent.inheritableThreadLocals != null) {            //给当前线程创建ThreadLocalMap对象,并且继承parent的ThreadLocalMap中的数据            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(                    parent.inheritableThreadLocals);        }}
说明,这里有两点需要注意下:
类加载器即ClassLoader,那么什么是类加载器?一个完整的Java程序可能由几个.class文件组成,程序在运行的过程中并不是一下子把所有的.class文件放置内存中,而是通过类加载器把需要的.class文件加载到内存中,进而其他的.class文件可访问到这个该.class文件。想了解更多关于ClassLoader,可以参考这篇《深入分析Java 类加载器》文章。
b:ThreadLocalMap 它是ThreadLocal的内部类,每个Thread都一个ThreadLocalMap,其结构是Map类型,key存储的是当前线程的ThreadLocal对象,value存储的是Object类型的变量,其作用使用ThreadLocal声明一个变量时,ThreadLocal会为每个线程创建这个变量的副本,当线程对这个变量进行操作时,互相之间不受影响,在一定程度上解决多线程不安全的问题。关于ThreadLocal以后会详细介绍。

三 生命周期

1线程状态

public enum State {        NEW,//被实例化之后,但还没有调用start启动        RUNNABLE,//调用了start函数之后就处于Runnable状态         BLOCKED,//调用join()、sleep()、wait()使线程处于Blocked状态        WAITING,//        TIMED_WAITING,//        TERMINATED;//}

(1) new

     new也称为新线程状态,通过new关键字实例化一个Thread对象就生成一个新线程。当线程处于"新线程"状态时,仅仅是一个空线程对象,它还没有分配到系统资源。因此只能启动或终止它。任何其他操作都会引发异常。例如,一个线程调用了new方法之后,并在调用start方法之前的处于新线程状态,可以调用start和stop方法。

(2)Runnable

     Runnable也称为可运行状态,通过start方法后使线程处于该状态,此时线程获取了支持其运行的资源,并调度其run方法,这个状态不能想当然的认为是运行状态,因为这时的线程并不总是一直占用处理机,它也有可能不在运行,这是因为还有优先级和调度问题特别是对于只有一个处理机的PC而言,任何时刻只能有一个处于可运行态的线程占用处理机。Java通过调度来实现多线程处理机的共享。
(3)NOT Runnable
    NOT Runnable也称为阻塞状态当以下事件发生时,线程处于该状态:
        a:调用supped、sleep方法
        b:调用wait方法等待条件变量
        c:线程处于I/O请求的等待

(4)Dead

      Dead也称为死亡状态,run方法运行完毕、其他线程调用该线程的stop方法、异常终止都会使线程处理该状态。

2 线程的操作:

派生:线程在进程内派生出来,它即可由进程派生,也可由线程派生。
阻塞(Block):如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。
激活(unblock):如果阻塞线程的事件发生,则该线程被激活并进入就绪队列。
调度(schedule):选择一个就绪线程进入执行状态
结束(Finish):如果一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放     
好,接下来借用网友一幅图加于说明:

四 函数分析

1 start函数分析

一个线程启动调用的start函数,下面我们看下它的源码:
Thread#start
public synchronized void start() {        /**         *threadStatus ==0,说明该线程状态为"NEW",还没有启动过,一个线程只能启动一次,否则抛出异常。         */        if (threadStatus != 0)            throw new IllegalThreadStateException();        /* 把当前线程添加到线程组中 */        group.add(this);        started = false;        try {            //调用native层函数创建线程,native层的源码未找到先不讨论它            nativeCreate(this, stackSize, daemon);            started = true;        } finally {            ......        }}
说明,这个方法主要两个作用:
a:把线程添加到线程组中
b:调用native层的nativeCreate函数完成线程的创建。

interrupt函数分析

Thread#interrupt
public void interrupt() {        if (this != Thread.currentThread())            checkAccess();        //blockerLock        synchronized (blockerLock) {            Interruptible b = blocker;            if (b != null) {                nativeInterrupt();                b.interrupt(this);                return;            }        }//调用native层函数完成线程中断        nativeInterrupt();}
说明,在Java中“中断”线程是通过interrupt()方法来实现的,之所以加引号,是因为interrupt()并不中断正在运行的线程,只是向线程发送一个中断请求,具体行为依赖于线程的状态,如下
a:如果线程处于阻塞状态,即线程被Object.wait()、Thread.join()或 Thread.sleep()阻塞,调用interrupt()方法,将接收到InterruptedException异常,中断状态被清除,结束阻塞状态;
b:如果线程在进行I/O操作(java.nio.channels.InterruptibleChannel)时被阻塞,那么线程将收到java.nio.channels.ClosedByInterruptException异常,通道被关闭,结束阻塞状态;
c:如果线程被阻塞在java.nio.channels.Selector中,那么中断状态会被置位并返回,不会抛出异常。

3 join函数分析

Thread#join
public final void join() throws InterruptedException {        join(0);}public final void join(long millis) throws InterruptedException {        synchronized(lock) {        long base = System.currentTimeMillis();        long now = 0;        if (millis < 0) {            throw new IllegalArgumentException("timeout value is negative");        }        if (millis == 0) {            while (isAlive()) {                lock.wait(0);            }        } else {            while (isAlive()) {                long delay = millis - now;                if (delay <= 0) {                    break;                }                lock.wait(delay);                now = System.currentTimeMillis() - base;            }        }        }}
说明,join()方法也可以理解为线程之间协作的一种方式,当两个线程需要顺序执行时,调用第一个线程的join()方法能使该线程阻塞,其依然通过wait()方法来实现的。

4 sleep与wait函数分析

Thread#sleep
public static void sleep(long millis) throws InterruptedException {        Thread.sleep(millis, 0);}
Thread#wait
public final void wait(long millis) throws InterruptedException {        wait(millis, 0);}
说明,sleep()与wait()的相同之处在于它们都是通过等待阻塞线程,不同之处在于sleep()等待的是时间,wait()等待的是对象的锁,在这里是看不出来的。

5 常用函数总结

run():包含线程运行时所执行的代码 

start()用于启动线程

sleep()、sleep(long millis)线程休眠,交出CPU,让CPU去执行其他的任务,然后线程进入阻塞状态,sleep方法不会释放锁

yield()使当前线程交出CPU,让CPU去执行其他的任务,但不会是线程进入阻塞状态,而是重置为就绪状态,yield方法不会释放锁

join()、join(long millis)、join(long millis,int nanoseconds)等待线程终止,直白的说 就是发起该子线程的线程 只有等待该子线程运行结束才能继续往下运行

wait()交出cpu,让CPU去执行其他的任务,让线程进入阻塞状态,同时也会释放锁

interrupt()中断线程,自stop函数过时之后,我们通过interrupt方法和isInterrupted()方法来停止正在运行的线程,注意只能中断已经处于阻塞的线程

getId()获取当前线程的ID

getName()、setName()获取和设置线程的名字

getPriority()setPriority()获取和这是线程的优先级 一般property用1-10的整数表示,默认优先级是5,优先级最高是10,优先级高的线程被执行的机率高

setDaemon()isDaemo()设置和判断是否是守护线程

currentThread()静态函数获取当前线程


参考文献
1《android 进程/线程管理(二)----关于线程的迷思
2《Android线程管理之Thread使用总结
3《Thread类的内部原理、休眠及唤醒

原创粉丝点击