并发与多线程开发核心技术

96 阅读8分钟

知识图谱

在这里插入图片描述

  • Android Thread
    在这里插入图片描述
    在这里插入图片描述

线程的几种创建方式

  • 通过Thread类或Runnable接口创建
    在这里插入图片描述
  • 通过Android 提供的AysncTask创建
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 通过HandlerThread
    在这里插入图片描述
    在这里插入图片描述
  • IntentService
    在这里插入图片描述
  • ThreadPoolExecutor
    在这里插入图片描述

线程的优先级

在这里插入图片描述
在这里插入图片描述

线程的几种状态与常用方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

线程间通信

  • 主线程向子线程发送消息
    在这里插入图片描述
    在这里插入图片描述

Android多线程开发核心知识点

什么是线程并发安全

线程安全的本质是 能够让并发线程有序的运行(这个有序有可能是先来后到排队,有可能有人插队,但不管怎么着,同一时刻只能一个线程有权访问同步资源),线程执行的结果,能够对其他线程可见。

线程安全的几种分类

  • synchronized关键字
  • ReentrantLook锁
  • AtomicInteger。。。原子类
    在这里插入图片描述
    在这里插入图片描述
  • synchronized, ReentrantLock锁
    在这里插入图片描述
  • 锁适合写操作多的场景,先加锁可以保证写操作时数据正确
  • 原子类适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。

如何保证线程安全

  • AtomicInteger原子包装类,CAS(Compare-And-Swap)实现无锁数据更新。自旋的设计能够有效避免线程因阻塞-唤醒带来的系统资源开销
  • 适用场景:多线程计数,原子操作,并发数量小的场景
    在这里插入图片描述
  • volatile可见性修饰
    volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存重新读取该成员的值,而且,当成员变量值发生变化时,强迫将变化的值重新写入共享内存,不能解决非原子操作的线程安全性。性能也不及原子类高
    在这里插入图片描述
  • 测试代码
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 一个用原子类修饰,一个用volatile修饰,在多线程的情况做自增,然后输出最后得值
 */
public class AtomicDemo {


    public static void main(String[] args) throws InterruptedException {
        final AtomicTask task = new AtomicTask();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    task.incrementVolatile();
                    task.incrementAtomic();
                }
            }
        };

        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("原子类的结果:" + task.atomicInteger.get());
        System.out.println("volatile修饰的结果:" + task.volatileCount);
    }

    static class AtomicTask {
        AtomicInteger atomicInteger = new AtomicInteger();
        volatile int volatileCount = 0;

        void incrementAtomic() {
            atomicInteger.getAndIncrement();
        }

        void incrementVolatile() {
            volatileCount++;
            //volatileCount = volatileCount + 1;
            //volatileCount = 10000;
        }
    }
}
  • synchronized
    锁Java对象,锁class对象,锁代码块
  • 锁方法,加在方法上,未获取到对象锁的其他线程都不可以访问该方法
    在这里插入图片描述
  • 锁class对象。加在static方法上相当于给class对象加锁,哪怕是不同的Java对象实例,也需要排队执行
    在这里插入图片描述
  • 锁代码块。未获取到对象锁的其他线程可以执行同步块之外的代码
    在这里插入图片描述
  • synchronized的优势是什么?
    哪怕我们一个同步方法中出现了异常,那么jvm也能够为我们自动释放锁,能主动规避死锁。不需要开发者手动释放锁
  • synchronized劣势是什么?
    必须等到获取锁对象的线程执行完成,或者出现异常,才能释放掉。不能中途释放锁,不能中断一个正在试图获得锁的线程
    另外我们也不知道多个线程竞争锁的时候,获取锁成功与否,所以不够灵活
    每个锁仅有单一的条件(某个对象)不能设定超时
  • ReentrantLock悲观锁,可重入锁,公平锁,非公平锁
    基本用法
    在这里插入图片描述
  • 测试代码
import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示 多个线程去竞争锁的 用法
 */
public class ReentrantLockDemo {

    static class ReentrantLockTask {
        ReentrantLock reentrantLock = new ReentrantLock();

        void buyTicket() {
            String name = Thread.currentThread().getName();
            try {
                reentrantLock.lock();
                System.out.println(name + ":准备好了");
                Thread.sleep(100);
                System.out.println(name + ":买好了");

                reentrantLock.lock();
                System.out.println(name + ":又准备好了");
                Thread.sleep(100);
                System.out.println(name + ":又买好了");

                reentrantLock.lock();
                System.out.println(name + ":准备好了");
                Thread.sleep(100);
                System.out.println(name + ":又买好了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final ReentrantLockTask task = new ReentrantLockTask();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.buyTicket();
            }
        };

        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }

}
  • 可重入锁,避免死锁
    在这里插入图片描述
  • 公平锁与非公平锁
  • 公平锁,所有进入阻塞的线程排队一次均有机会执行
  • 默认非公平锁,允许线程插队,避免每一个线程都进入阻塞,再唤醒,性能高。因为线程可以插队,导致队列中可能会存在线程饿死的情况,一直得不到锁,一直得不到执行。
  • 传入false即为非公平锁
    在这里插入图片描述
import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示  多个线程 去打印纸张,每个线程 打印张(ReentrantLock 公平锁,非公平锁)
 * <p>
 * 公平锁:交易,比如先买票的人先进场  
 * 非公平锁:synchorinzed,场景比比皆是
 */
public class ReentrantLockDemo2 {

    static class ReentrantLockTask {

        ReentrantLock lock = new ReentrantLock(false
        );

        void print() {
            String name = Thread.currentThread().getName();
            try {
                lock.lock();
                //打印两次
                System.out.println(name + "第一次打印");
                Thread.sleep(1000);
                lock.unlock();

                lock.lock();
                System.out.println(name + "第二次打印");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    }

    public static void main(String[] args) {

        final ReentrantLockTask task = new ReentrantLockTask();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.print();
            }
        };

        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }

}
  • ReentrantLock进阶用法–Condition条件对象
    可使用它的await-singnal指定唤醒一个(组)线程。相比于wait-notify要么全部唤醒,要么只能唤醒一个,更加灵活可控

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 我们演示 生产者与消费者的场景,利用的是ReentrantLock condition 条件对象,能够指定唤醒某个线程去工作
 * <p>
 * <p>
 * 生产者是:一个boss  去生产砖,砖的序列号为偶数,那么工人2去搬,奇数号让工人去去搬
 * <p>
 * 消费者是两个工人,有砖搬就搬,没转搬就休息
 */
public class ReentrantLockDemo3 {

    static class ReentrantLockTask {

        private Condition worker1Condition, worker2Condition;
        ReentrantLock lock = new ReentrantLock(true);

        volatile int flag = 0;//砖的序列号

        public ReentrantLockTask() {
            worker1Condition = lock.newCondition();
            worker2Condition = lock.newCondition();
        }

        //工人1搬砖
        void work1() {
            try {
                lock.lock();
                if (flag == 0 || flag % 2 == 0) {
                    System.out.println("worker1 无砖可搬,休息会");
                    worker1Condition.await();
                }

                System.out.println("worker1 搬到的砖是:" + flag);
                flag = 0;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }


        //工人2搬砖
        void work2() {
            try {
                lock.lock();
                if (flag == 0 || flag % 2 != 0) {
                    System.out.println("worker2 无砖可搬,休息会");
                    worker2Condition.await();
                }

                System.out.println("worker2 搬到的砖是:" + flag);
                flag = 0;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        void boss() {

            try {
                lock.lock();
                flag = new Random().nextInt(100);
                if (flag % 2 == 0) {
                    worker2Condition.signal();
                    System.out.println("生产出来了砖,唤醒工人2去搬:" + flag);
                } else {
                    worker1Condition.signal();
                    System.out.println("生产出来了砖,唤醒工人1去搬:" + flag);
                }
            } finally {
                lock.unlock();
            }
        }


        public static void main(String[] args) {
            final ReentrantLockTask lockTask = new ReentrantLockTask();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        lockTask.work1();
                    }
                }
            }).start();


            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        lockTask.work2();
                    }
                }
            }).start();


            for (int i = 0; i < 10; i++) {
                lockTask.boss();
            }
        }
    }
}
  • ReentrantReadWriteLock共享锁,排他锁
  • 共享锁,所有线程均可同时获得,并发量高,比如在线文档查看
  • 排他锁,统一时刻只有一个线程有权修改资源,比如在线文档编辑
    在这里插入图片描述
  • 测试代码
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 利用ReentrantReadWriteLock  来实现 多人在线文档查看与编辑的功能
 */
public class ReentrantReadWriteLockDemo {

    static class ReentrantReadWriteLockTask {
        private final ReentrantReadWriteLock.ReadLock readLock;
        private final ReentrantReadWriteLock.WriteLock writeLock;
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

        ReentrantReadWriteLockTask() {
            readLock = lock.readLock();
            writeLock = lock.writeLock();
        }

        void read() {
            String name = Thread.currentThread().getName();
            try {
                readLock.lock();
                System.out.println("线程" + name + " 正在读取数据...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
                System.out.println("线程" + name + " 释放了读锁...");
            }
        }


        void write() {
            String name = Thread.currentThread().getName();
            try {
                writeLock.lock();
                System.out.println("线程" + name + " 正在写入数据...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
                System.out.println("线程" + name + " 释放了写锁...");
            }
        }

        public static void main(String[] args) {

            final ReentrantReadWriteLockTask task = new ReentrantReadWriteLockTask();


            for (int i = 0; i < 3; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        task.read();
                    }
                }).start();
            }


            for (int i = 0; i < 3; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        task.write();
                    }
                }).start();
            }
        }
    }
}

如何正确的使用锁&原子类

  • 减少持锁时间
    尽管锁在同一时间只能允许一个线程持有,其他想要占用锁的线程都得在临界区外等待锁的释放,这个等待的时间我们希望尽可能的短。
    在这里插入图片描述
  • 锁分离
    读读,读写,写读,写写。只要有写锁进入才需要做同步处理,但是在大多数应用来说,读的场景要远远大于写的场景,因此一旦使用读写锁,在读多写少的场景中,就可以很好的提高系统的性能。
“ ”读锁写锁
读锁可以访问不可访问
写锁不可访问不可访问
  • 锁粗化
    多次加锁,释放锁合并成一次
    在这里插入图片描述
    在这里插入图片描述

深入理解Android线程池实现原理

为什么要引入线程池

在这里插入图片描述

Java中几种默认的线程池

  • 如何构建线程池
    在这里插入图片描述
  • 线程池创建参数说明
    在这里插入图片描述
  • JUC包下Executors提供的几种线程池
    在这里插入图片描述
  • 线程池重要方法
    在这里插入图片描述
  • 线程池状态流转
    在这里插入图片描述
  • execute提交任务流程
    在这里插入图片描述
  • 源码分析部分略过

实战:封装一个简洁易用的多线程操作框架

  • 需求分析
    设计一个全局通用的线程池组件-MyExecutor

    1. 支持任务优先级
    2. 支持线程暂停、恢复、关闭
    3. 支持异步任务结果回调
  • Coding实现

  • 线程池参数构造
    在这里插入图片描述

  • 实现线程池中任务按优先级执行
    代码没截全,剩余部分应该能自己补出来
    在这里插入图片描述

  • 实现线程池的暂停、恢复
    在这里插入图片描述

  • 实现异步任务结果主动切换到主线程
    在这里插入图片描述

  • 测试代码

import android.content.ContentValues.TAG
import android.os.Handler
import android.os.Looper
import androidx.annotation.IntRange
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.ReentrantLock
import kotlin.math.max

/**
 * 支持按任务的优先级去执行,
 * 支持线程池暂停.恢复(批量文件下载,上传) ,
 * 异步结果主动回调主线程
 * todo 线程池能力监控,耗时任务检测,定时,延迟,
 */
object HiExecutor {
    private const val TAG: String = "HiExecutor"
    private var isPaused: Boolean = false
    private var hiExecutor: ThreadPoolExecutor
    private var lock: ReentrantLock = ReentrantLock()
    private var pauseCondition: Condition
    private val mainHandler = Handler(Looper.getMainLooper());

    init {
        pauseCondition = lock.newCondition()

        val cpuCount = Runtime.getRuntime().availableProcessors()
        val corePoolSize = cpuCount + 1
        val maxPoolSize = cpuCount * 2 + 1
        val blockingQueue: PriorityBlockingQueue<out Runnable> = PriorityBlockingQueue()
        val keepAliveTime = 30L
        val unit = TimeUnit.SECONDS

        val seq = AtomicLong()
        val threadFactory = ThreadFactory {
            val thread = Thread(it)
            //hi-executor-0
            thread.name = "hi-executor-" + seq.getAndIncrement()
            return@ThreadFactory thread
        }

        hiExecutor = object : ThreadPoolExecutor(
            corePoolSize,
            maxPoolSize,
            keepAliveTime,
            unit,
            blockingQueue as BlockingQueue<Runnable>,
            threadFactory
        ) {
            override fun beforeExecute(t: Thread?, r: Runnable?) {
                if (isPaused) {
                    lock.lock()
                    try {
                        pauseCondition.await()
                    } finally {
                        lock.unlock()
                    }
                }
            }

            override fun afterExecute(r: Runnable?, t: Throwable?) {
                //监控线程池耗时任务,线程创建数量,正在运行的数量
                HiLog.e(TAG, "已执行完的任务的优先级是:" + (r as PriorityRunnable).priority)
            }
        }
    }

    fun execute(@IntRange(from = 0, to = 10) priority: Int = 0, runnable: Runnable) {
        hiExecutor.execute(PriorityRunnable(priority, runnable))
    }

    abstract class Callable<T> : Runnable {
        override fun run() {
            mainHandler.post { onPrepare() }

            val t: T? = onBackground()

            //移除所有消息.防止需要执行onCompleted了,onPrepare还没被执行,那就不需要执行了
            mainHandler.removeCallbacksAndMessages(null)
            mainHandler.post { onCompleted(t) }
        }

        open fun onPrepare() {
            //转菊花
        }

        abstract fun onBackground(): T?
        abstract fun onCompleted(t: T?)
    }

    class PriorityRunnable(val priority: Int, private val runnable: Runnable) : Runnable,
        Comparable<PriorityRunnable> {
        override fun compareTo(other: PriorityRunnable): Int {
            return if (this.priority < other.priority) 1 else if (this.priority > other.priority) -1 else 0
        }

        override fun run() {
            runnable.run()
        }

    }


    fun pause() {
        lock.lock()
        try {
            isPaused = true
            HiLog.e(TAG, "hiExecutor is paused")
        } finally {
            lock.unlock()
        }
    }

    fun resume() {
        lock.lock()
        try {
            isPaused = false
            pauseCondition.signalAll()
        } finally {
            lock.unlock()
        }
        HiLog.e(TAG, "hiExecutor is resumed")
    }
}

Kotlin协程机制

什么是协程

  • 场景1:异步回调嵌套
    在这里插入图片描述
  • 协程的写法
    在这里插入图片描述
  • 场景2:并发流程控制
    在这里插入图片描述
  • 常规写法
    在这里插入图片描述
  • 协程写法
    在这里插入图片描述
    协程的目的是为了让多个任务之间更好的协作,解决异步回调嵌套。能够以同步的方式编排代码完成异步工作。将异步代码像同步代码一样直观。同时它也是一个并发流程控制的解决方案。
    协程主要是让原来使用“异步+回调”写出来的复杂代码,简化成看似同步写出来的方式,弱化了线程的概念(对线程的操作进一步抽象)。

协程的用法

  • 引入gradle依赖
    在这里插入图片描述
  • 常用的创建协程的方法
    在这里插入图片描述
    在这里插入图片描述
  • 测试代码
import android.util.Log
import kotlinx.coroutines.*


object CoroutineScene {

    private val TAG: String = "CoroutineScene"

    /**
     * 以此启动三个子线程, 并且同步的方式拿到他们的返回值,进而更新UI
     */
    fun startScene1() {
        GlobalScope.launch(Dispatchers.Unconfined) {
            Log.e(TAG, "coroutine is running")
            val result1 = request1()
            val result2 = request2(result1)
            val result3 = request3(result2)

            updateUI(result3)
        }

        Log.e(TAG, "coroutine has launched")
    }

    /**
     * 启动一个线程,先执行request1,完了之后,同时运行request2 和request3, 这俩并发都结束了才执行updateIU
     */
    fun startScene2() {

        GlobalScope.launch(Dispatchers.Main) {

            Log.e(TAG, "coroutine is running")

            val result1 = request1()
//           request2()
//           request3()

            val deferred2 = GlobalScope.async { request2(result1) }
            val deferred3 = GlobalScope.async { request3(result1) }

            // deferred2.await();
            //deferred3.await()
            updateUI(deferred2.await(), deferred3.await())
        }

        Log.e(TAG, "coroutine has started")
    }

    private fun updateUI(result2: String, result3: String) {
        Log.e(TAG, "updateui work on ${Thread.currentThread().name}")
        Log.e(TAG, "paramter:" + result3 + "---" + result2)
    }

    private fun updateUI(result3: String) {
        Log.e(TAG, "updateui work on ${Thread.currentThread().name}")
        Log.e(TAG, "paramter:" + result3)
    }

    //suspend关键字的作用--->
    //delay既然是IO异步任务,是如何做到延迟协程 中的代码向下执行的?
    suspend fun request1(): String {
        delay(2 * 1000)  //不会暂停线程,但会暂停当前所在的协程
        //Thread.sleep(2000)  让线程休眠

        Log.e(TAG, "request1 work on ${Thread.currentThread().name}")
        return "result from request1"
    }

    suspend fun request2(result1: String): String {
        delay(2 * 1000)

        Log.e(TAG, "request2 work on ${Thread.currentThread().name}")
        return "result from request2"
    }


    suspend fun request3(result2: String): String {
        delay(2 * 1000)

        Log.e(TAG, "request3 work on ${Thread.currentThread().name}")
        return "result from request3"
    }

}

协程挂起,恢复原理逆向剖析

  • 挂起函数
    被关键字suspend修饰的方法在编译阶段,编译器会修改方法的签名,包括返回值,修饰符,入参,方法实体。协程的挂起是靠挂起函数中实现的代码。
  • Kotlin代码
    在这里插入图片描述
  • 反编译后生成的Java代码
    在这里插入图片描述
  • 协程挂起与协程恢复
    在这里插入图片描述
  • 实例代码

import android.util.Log
import kotlinx.coroutines.delay

object CoroutineScene2 {

    private val TAG: String = "CoroutineScene2"

    suspend fun request1(): String {
        val request2 = request2();
        return "result from request1 " + request2;
    }

    suspend fun request2(): String {
        delay(2 * 1000)
        Log.e(TAG, "request2 completed")
        return "result from request2"
    }
}
  • 使用Java代码实现协程的suspend功能

import android.util.Log;

import org.jetbrains.annotations.NotNull;

import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlinx.coroutines.DelayKt;

/**
 * suspend fun request1(): String {
 * val request2 = request2();
 * return "result from request1 " + request2;
 * }
 * <p>
 * <p>
 * suspend fun request2(): String {
 * delay(2 * 1000)
 * Log.e(TAG, "request2 completed")
 * return "result from request2"
 * }
 */
public class CoroutineScene2_decompiled {

    private static final String TAG = "CoroutineScene2";

    public static final Object request1(Continuation preCallback) {

        ContinuationImpl request1Callback;
        if (!(preCallback instanceof ContinuationImpl) || (((ContinuationImpl) preCallback).label & Integer.MIN_VALUE) == 0) {
            request1Callback = new ContinuationImpl(preCallback) {

                @Override
                Object invokeSuspend(@NotNull Object resumeResult) {
                    this.result = resumeResult;
                    this.label |= Integer.MIN_VALUE;
                    Log.e(TAG, "request1 has resumed");
                    return request1(this);
                }
            };
        } else {
            request1Callback = (ContinuationImpl) preCallback;
        }

        switch (request1Callback.label) {
            case 0: {
                // Object delay = DelayKt.delay(2000, request1Callback);
                Object request2 = request2(request1Callback);
                if (request2 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
                    Log.e(TAG, "request1 has suspended");
                    return IntrinsicsKt.getCOROUTINE_SUSPENDED();
                }
            }
        }

        Log.e(TAG, "request1 completed");
        return "result from request1" + request1Callback.result;
    }

    public static final Object request2(Continuation preCallback) {

        ContinuationImpl request2Callback;
        if (!(preCallback instanceof ContinuationImpl) || (((ContinuationImpl) preCallback).label & Integer.MIN_VALUE) == 0) {
            request2Callback = new ContinuationImpl(preCallback) {

                @Override
                Object invokeSuspend(@NotNull Object resumeResult) {
                    this.result = resumeResult;
                    this.label |= Integer.MIN_VALUE;
                    Log.e(TAG, "request2 has resumed");
                    return request2(this);
                }
            };
        } else {
            request2Callback = (ContinuationImpl) preCallback;
        }

        switch (request2Callback.label) {
            case 0: {
                Object delay = DelayKt.delay(2000, request2Callback);
                if (delay == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
                    Log.e(TAG, "request2 has suspended");
                    return IntrinsicsKt.getCOROUTINE_SUSPENDED();
                }
            }
        }

        Log.e(TAG, "request2 completed");
        return "result from request2";
    }


    static abstract class ContinuationImpl<T> implements Continuation<T> {
        private Continuation preCallback;
        int label;
        Object result;

        public ContinuationImpl(Continuation preCallback) {
            this.preCallback = preCallback;
        }

        @NotNull
        @Override
        public CoroutineContext getContext() {
            return preCallback.getContext();
        }

        @Override
        public void resumeWith(@NotNull Object resumeResult) {
            Object suspend = invokeSuspend(resumeResult);
            if (suspend == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
                return;
            }
            preCallback.resumeWith(suspend);
        }

        abstract Object invokeSuspend(@NotNull Object resumeResult);
    }
}

协程回顾

  • 什么是协程
    协程是一种解决方案,是一种解决嵌套,并发,弱化线程概念的方案。能让多个任务之间更好的协作,能够以同步的方式编排代码完成异步工作。将异步代码写的像同步代码一样直观
  • 协程的启动
    根据创建协程指定的调度器 HandlerDispatcher,DefaultScheduler,UnconfinedDispatcher来执行任务,以决定协程中的代码块运行在那个线程上
  • 协程的挂起、恢复
    本质是方法的挂起、恢复。本质是return+Callback。
    用编译时的变换处理方法间的Callback,这样可以很直观地写顺序执行的异步代码。
  • 协程是线程框架么?
    协程的本质是编译时return+Callback。只不过在调度任务时提供了能够运行在IO线程的调度器。
  • 什么时候使用协程?
    多任务并发流程控制场景使用比较好,流程控制比较简单,不会涉及线程阻塞与唤醒,性能比Java并发控制手段高。

Kotlin协程应用

需求分析:如何让普通函数适配协程,成为“真正的挂起函数”。即让调用方以同步的方式拿到异步任务返回结果

import android.content.res.AssetManager
import android.util.Log
import kotlinx.coroutines.suspendCancellableCoroutine
import java.io.BufferedReader
import java.io.InputStreamReader
import java.lang.StringBuilder

/**
 * 演示以异步的方式 读取 asstes目录下的文件,并且适配协程的写法,让他真正的挂起函数
 *
 * 方便调用方 直接以同步的形式拿到返回值
 */
object CoroutineScene3 {
    suspend fun parseAssetsFile(assetManager: AssetManager, fileName: String): String {
       // suspendCoroutine<> {  }
        return suspendCancellableCoroutine { continuation ->
            Thread(Runnable {
                val inputStream = assetManager.open(fileName)
                val br = BufferedReader(InputStreamReader(inputStream))
                var line: String?
                var stringBuilder = StringBuilder()

//               while ((line=br.readLine())!=null){
//
//               }

                do {
                    line = br.readLine()
                    if (line != null) stringBuilder.append(line) else break
                } while (true)

                inputStream.close()
                br.close()

                Thread.sleep(2000)

                Log.e("coroutine", "parseassetsfile completed")
                continuation.resumeWith(Result.success(stringBuilder.toString()))
            }).start()
        }
    }
}

如何做多线程优化

线程池

在这里插入图片描述

并发安全

在这里插入图片描述
在这里插入图片描述

  • 锁粗化
    在这里插入图片描述

线程协作

在这里插入图片描述

  • 实例代码
import java.util.Random;
import java.util.concurrent.CountDownLatch;

/**
* 演示一个 多人过山车的场景.
* <p>
* 我们假设有5人 去乘坐做过山车,----等待5人全部准备好,才能发车
*/
public class CountDownLatchDemo {

   public static void main(String[] args) throws InterruptedException {
       final CountDownLatch downLatch = new CountDownLatch(5);
       for (int i = 0; i < 5; i++) {
           new Thread(new Runnable() {
               @Override
               public void run() {
                   try {
                       Thread.sleep(new Random().nextInt(4000));
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName() + "准备好了");
                   downLatch.countDown();
               }
           }).start();
       }

       downLatch.await();
       System.out.println("所有人都准备好了,准备发车...");
   }
}

在这里插入图片描述

  • 示例代码
import java.util.Random;
import java.util.concurrent.Semaphore;

/**
 * 演示 多人故宫游玩,但是同一时刻限流3人
 */
public class SemaphoreDemo {
    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(3, true);
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        String name = Thread.currentThread().getName();
                        semaphore.acquire(2);

                        System.out.println(name + "获取到了许可证,进去游玩了");

                        Thread.sleep(new Random().nextInt(5000));

                        semaphore.release(2);

                        System.out.println(name + "归还了许可证");

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    }
}

协程

在这里插入图片描述