美文网首页
Android中的Thread详解

Android中的Thread详解

作者: BlueSocks | 来源:发表于2023-11-16 18:30 被阅读0次

    一、前言

    线程(英语:thread)在计算机科学中,是将进程划分为两个或多个线程(实例)或子进程,由单处理器(单线程)或多处理器(多线程)或多核处理系统并发执行。 —— 维基百科

    无论在Android还是java开发甚至其他语言开发,大多数都逃不过多线程的话题,当然dart等语言除外(不过也有isolate的概念)。

    线程是操作系统CPU资源分配调度的一个单元,在Java中,最常见的就是Thread类,通过该类可以让我们在多线程创建、启动、执行等操作更加得心应手。

    二、了解

    • 在Thread类中,只有run函数是运行在线程上。
    • 通过start()启动线程,实际上会调用native的VMThread.create(this, stackSize)方法创建CPU线程

    三、使用方法

    1.通过继承Thread启动一个线程

    继承Thread,并复写run方法即可

    class CustomThread : Thread() {
        override fun run() {
           //执行一些耗时操作
            for(i in 0..1000){
                println("work $i")
            }
        }
    }
    
    fun main() {
        val customThread = CustomThread()
        customThread.start()
    }
    
    
    

    2.通过构造函数传入

    直接通过构造函数,传入自定义的Runnable接口

    fun main() {
        val thread = Thread({
            //执行一些耗时操作
            for (i in 0..1000) {
                println("work $i")
            }
        }, "CustomThread")
    
        thread.start()
    }
    
    

    四、详解

    1.启动流程

    thread.start()是线程启动的入口,只有在start执行时真正创建了native线程,其他准备工作(比如设置name、runnable等)都是在java的当前线程执行处理。

    这里Android进行了改造,将start0改成了nativeCreate开启线程。

    注意这里做了实例的同步锁。

    源码:

    public synchronized void start() {
     
        // 禁止多次调用start()方法,重复开始抛异常
        if (started)
            throw new IllegalThreadStateException();
      
        group.add(this);
      
        // STUDY: 2023/11/11 保证字段正确性
        started = false;
        try {
            // Android的改造
            // Android-changed: Use Android specific nativeCreate() method to create/start thread.
            // start0();
            nativeCreate(this, stackSize, daemon);
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    
    
    private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
    
    

    2.run流程

    主要是执行传入的runnable接口回调。只有在runnable中执行的才是在新线程,其他的都是在当前线程。

    由start执行后,由JVM自动调用run方法在新线程中。

    源码:

    private Runnable target;
    
    ...
      
    private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc){
        ...
        this.target = target
        ...
    }
    
    ...
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    
    

    3.wait

    wait()为Object的公有方法,使用时需要配合synchronized和Object实例调用,用来阻塞当前线程。此时释放掉synchronized的锁。如果被唤醒也是需要"随机"竞争锁。

    • 在哪个线程执行,就会阻塞那个线程
    • 必须在synchronized同步代码块中调用
    • wait时会释放掉同步锁
    • 仅释放调用wait方法的锁,不会改变其他锁的状态
    • 会有InterruptedException异常

    参数:

    • wait():内部调用wait(0),表示无超时时间,一直等待
    • wait(long timeout):内部调用wait(timeout, 0)
    • wait(long timeout, int nanos):native方法,进入等待状态,如果是0为一直等待,直到收到唤醒通知

    举例:

    val lock = Object()
    
    ...
    
    private fun sendWait(){
        synchronized(lock){
            try {
                // 阻塞当前线程,但会释放调用的lock的锁
                lock.wait()
            } catch (e :InterruptedException) {
                e.printStackTrace()
            }
        }
    }
    
    

    4.notify、notifyAll

    notify()、notifyAll()为Object的公有方法,使用时需要配合synchronized和Object实例调用,用来通知唤醒阻塞线程。notify不会有释放锁操作,仅仅通知wait状态的线程可以去竞争锁,而不是立马拿到,需要等到notify代码块执行完毕释放锁。notify为随机唤醒一个阻塞线程,notifyAll为唤醒所有阻塞线程。

    • notify操作不会有释放锁操作,仅作为通知
    • notify为随机通知一个wait唤起
    • notifyAll通知所有wait状态唤起
    • 会有InterruptedException异常

    举例:

    val lock = Object()
    
    ...
    
    private fun sendNotify(){
        synchronized(lock){
            try {
                // 随机通知一个阻塞唤醒
                lock.notify()
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
        }
    }
    
    
    val lock = Object()
    
    ...
    
    private fun sendNotifyAll(){
        synchronized(lock){
            try {
                // 通知所有阻塞唤醒,但也要竞争锁
                lock.notifyAll()
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
        }
    }
    
    

    5.sleep

    sleep()为Thread的静态方法。使当前线程睡眠指定一段时间,之后自行恢复,notify对此无用。此时会持有thread的lock对象锁,线程休眠也不会释放其他锁。

    • 在哪个线程执行,就会阻塞那个线程。
    • 睡眠期间不会释放所有锁
    • 会有InterruptedException异常

    参数:

    • sleep(long millis):调用sleep(millis, 0)
    • sleep(long millis, int nanos):线程睡眠

    案例:

    private fun sendSleep(){
        try {
            // 这里会阻塞3s中,如果期间中断会被捕获异常
            Thread.sleep(3000)
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }
    
    

    源码:

    public static void sleep(long millis, int nanos)
        throws InterruptedException {
           
            ...
                
            // 如果无需睡眠,不做任何处理,中断状态下会抛出异常。
            if (millis == 0 && nanos == 0) {
                if (Thread.interrupted()) {
                  throw new InterruptedException();
                }
                return;
            }
    
            ...
    
            // 获取当前线程的Object锁对象
            Object lock = currentThread().lock;
    
            // sleep 此时一直持有锁,直到睡眠结束
            synchronized (lock) {
                // 循环保证睡眠时间执行完全
                for (long elapsed = 0L; elapsed < durationNanos;elapsed = System.nanoTime() - startNanos) {
                    
                    ...
                        
                    //  私有方法  
                    sleep(lock, millis, nanos);
                }
            }
        }
    
    
    @FastNative
    private static native void sleep(Object lock, long millis, int nanos) throws InterruptedException;
    
    

    6.interrupt

    interrupt为Thread的公有方法。线程中断后,可通过isInterrupted()进行判断是否处理中断状态。

    • 未start()的线程,调用interrupt不影响,后续可正常操作
    • 要在中断前执行特殊操作,需要设置 blockedOn(Interruptible b)
    • 仅仅是打上中断标志,而非立刻停止。

    需要以下做中断try catch,否则造成程序crash。

    • Thread.sleep()
    • object.wait()
    • object.notify()
    • object.notifyAll()
    • thread.join()

    举例:

    val thread: Thread? = null
    
    ...
    
    fun sendInterrupt(){
        thread?.interrupt()
    }
    
    

    源码:

    public void interrupt() {
        // 如果不是当前线程,进行判断
        if (this != Thread.currentThread())
            // Android相关移除了非同线程的操作异常,空方法
            checkAccess();
    
        // 通过blockerLock同步锁进行中断操作,防止设置不同步
        synchronized (blockerLock) {
            // 由blockedOn设置
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                // 中断前的回调操作
                b.interrupt(this);
                return;
            }
        }
        // native层标记,而非立即中断
        interrupt0();
    }
    
    
    // 缓存set的中断前回调
    private volatile Interruptible blocker;
    // 设置和执行blocker的同步锁
    private final Object blockerLock = new Object();、
        
    ...
        
    /** @hide */
    // 设置中断回调
    public void blockedOn(Interruptible b) {
        synchronized (blockerLock) {
            blocker = b;
        }
    }
    
    

    7.join

    join()是Thread的公有方法,等待线程任务的完成。可重复执行,如果线程销毁了,相当于直接完成。这里会阻塞当前执行线程,对于Android来说,千万不要再主线程去调用防止造成ANR。

    参数:

    • join():内部调用了join(0),无限制等待。直到任务完成
    • join(long millis, int nanos):内部计算处理完具体时间后,调用join(long millis)
    • join(long millis):最终调用方法,如果为0,无限时间等待;否则设置对应时间等待,到时自动结束

    案例:

    val thread: Thread? = null
    val TAG: String = "Thread"
    ...
    
    fun sendJoin(){
        try {
            //阻塞当前线程,等待任务完成
            thread?.join()
            Log.d(TAG, "thread end, join finish")
        } catch (e: InterruptedException){
            e.printStackTrace()
        }
    }
    
    

    源码:

    public final void join(long millis)
        throws InterruptedException {
            // 使用lock同步锁进行阻塞
        synchronized(lock) {
            long base = System.currentTimeMillis();
            long now = 0;
    
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (millis == 0) {
                while (isAlive()) {
                    // 无限时间等待。实际完成后cpp代码有调用notify相关唤醒这里
                    lock.wait(0);
                }
            } else {
                while (isAlive()) {
                    // 现在时间小于预期时间,直接跳出循环,结束方法体
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    // 有限时间等待,超时会自动唤醒跳出。实际cpp代码有调用notify相关唤醒这里
                    lock.wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }
    }
    
    

    8.yield

    让步调度,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)。同样不会释放锁

    五、状态图解

    线程状态有5种:

    • new: 实例化之后状态,没有start
    • runnable:start()之后启动,等待cpu调度执行,非立刻执行
    • blocked:阻塞状态,比如等锁
    • waiting:无限时间等待状态
    • timed_waiting:有限时间等待状态
    • terminated:完成或者异常,终止运行

    1. 正常线程执行状态:

    graph LR
        new(new)
        runnable(runnable)
        running(running)
        terminated(terminated)
        
        new --> runnable ---->|被CPU调用中| running --> terminated
    

    2. waiting状态线程:

    graph LR
        new(new)
        runnable(runnable)
        running(running)
        waiting(waiting)
        terminated(terminated)
        
        new --> runnable -->|被CPU调用中| running --> terminated
        running -.->|"object.wait() or object.join()"| waiting -.->|"object.notify() or object.notifyAll()" | runnable
    

    3. blocked状态:

    graph LR
        new(new)
        runnable(runnable)
        running(running)
        blocked(blocked)
        terminated(terminated)
        
        new --> runnable -->|被CPU调用中| running --> terminated
        running -.->|"等待同步锁状态"| blocked -.->|"拿到同步锁" | runnable
    

    4. timed_waiting状态:

    graph LR
        new(new)
        runnable(runnable)
        running(running)
        timed_waiting(timed_waiting)
        terminated(terminated)
        
        new --> runnable -->|被CPU调用中| running --> terminated
        running -.->|"sleep(*)或者wait(*)或者join(*)"| timed_waiting -.->|"时间到或者notify()或者notifyAll()"| runnable
    

    相关文章

      网友评论

          本文标题:Android中的Thread详解

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