android线程管理二(Thread)

前言

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

一 结构关系

public class Thread implements Runnable {
  ......
}
很显然 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值