多线程知识 汇总(1)

IntentService 
多线程的应用在Android  开发中是非常常见的,常用方法主要有:
  • 集成Thread类
  • 实现Runnable接口
  • AsyncTask
  • Handler
  • HandlerThread
  • IntentService
IntentService
定义: Android 里的一个封装类,继承四大组件之一 service
作用:处理异步请求 & 实现多线程
使用场景:线程任务 按照顺序,在后台执行。
        最常见的场景,离线下载,但是不符合多个数据同时请求的场景,所有任务都在同一个Thread looper 里执行。
特别注意
  1. 若启动IntentService多次,那么每个耗时操作则以队列的方式在 IntentService 的 onHandleIntent回调方法中依次执行,执行完自动结束。
问题1:IntentService 如何单独开启1个新的工作线程
@Override
public void onCreate() {
    super.onCreate();
    
    // 1. 通过实例化HandlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
    // HandlerThread继承自Thread,内部封装了 Looper
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
  
    // 2. 获得工作线程的 Looper & 维护自己的工作队列
    mServiceLooper = thread.getLooper();
    // 3. 新建mServiceHandler & 绑定上述获得Looper
    // 新建的Handler 属于工作线程 ->>分析1
    mServiceHandler = new ServiceHandler(mServiceLooper);
}
   /**
     * 分析1:ServiceHandler源码分析
     **/
     private final class ServiceHandler extends Handler {
         // 构造函数
         public ServiceHandler(Looper looper) {
         super(looper);
       }
        // IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
        @Override
         public void handleMessage(Message msg) {
  
          // onHandleIntent 方法在工作线程中执行
          // onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
          onHandleIntent((Intent)msg.obj);
          // 执行完调用 stopSelf() 结束服务
          stopSelf(msg.arg1);
    }
}
   /**
     * 分析2: onHandleIntent()源码分析
     * onHandleIntent() = 抽象方法,使用时需重写
     **/
      @WorkerThread
      protected abstract void onHandleIntent(Intent intent);
问题2 IntentService 如何通过onStartCommand() 将intent传递给服务, 依次插入到工作队列中的。
/**
  * onStartCommand()源码分析
  * onHandleIntent() = 抽象方法,使用时需重写
  **/
  public int onStartCommand(Intent intent, int flags, int startId) {
    // 调用onStart()->>分析1
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
/**
  * 分析1:onStart(intent, startId)
  **/
  public void onStart(Intent intent, int startId) {
    // 1. 获得ServiceHandler消息的引用
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    // 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
    //这里的Intent  = 启动服务时startService(Intent) 里传入的 Intent
    msg.obj = intent;
    // 3. 发送消息,即 添加到消息队列里
    mServiceHandler.sendMessage(msg);
}
源码总结:
IntentService 本质 是  Handler + HanderThread
  1. 通过handlerThread单独开启一个工作线程,IntentService
  2. 创建一个内部Handler:ServiceHandler
  3. 绑定serviceHandler 与 IntentService
  4. 通过onStartCommand() 传递intent到ServiceHandler,一次插入Intent到工作队列中。并且逐个发送到给HanderIntent
  5. 通过onHandlerIntent ()依次处理所有Intent对象所对应的任务。
线程 关键字: Synchronized 
它是java中一个关键字
被他修饰的方法可以保证同一时刻最多只有一个线程执行此方法,其他线程必须等待当前线程执行完该方法/代码块后才能执行该方法。
保证线程安全,解决多线程中的并发同步问题 实现的是阻塞型并发
原理: 
  • 依赖JVM实现同步
  • 底层通过一个监视器对象 monitor 完成,wait()、notify()等方法也依赖于  这个监视器对象 monitor  
monitor 监视器锁的本质,依赖于底层操作系统的互斥锁 实现。
其他控制并发/ 现成同步的方法
Lock       ReentrantLock
CAS   compre and Swap   乐观锁
// CAS的操作参数
内存位置(A)
预期原值(B)
预期新值(C)
// 使用CAS解决并发的原理:
// 1. 首先比较A、B,若相等,则更新A中的值为C、返回True;若不相等,则返回false;
// 2. 通过死循环,以不断尝试尝试更新的方式实现并发
// 伪代码如下
public boolean compareAndSwap(long memoryA, int oldB, int newC){
    if(memoryA.get() == oldB){
        memoryA.set(newC);
        return true;
    }
    return false;
}
优点: 耗费资源少,相对于synchronized  ,省去了挂起现成,回复线程的开销,但是迟迟得不到更新,死循环对CPU资源也是一种浪费
考点:线程与进程的区别
假设:进程 = 桌子,单线程 = 1个人吃饭
  • 单进程、单线程:一个人在一个桌子上吃饭
  • 单进程、多线程:多个人在同一个桌子上一起吃饭
  • 多进程、单线程:多个人每个人在自己的桌子上吃饭
一个进程可以拥有多个线程  ,多个线程可以共享他们隶属的同意进程的系统资源

ThreadLocal

// 1. 直接创建对象
private ThreadLocal myThreadLocal = new ThreadLocal()
// 2. 创建泛型对象
private ThreadLocal myThreadLocal = new ThreadLocal<String>();
// 3. 创建泛型对象 & 初始化值
// 指定泛型的好处:不需要每次对使用get()方法返回的值作强制类型转换
private ThreadLocal myThreadLocal = new ThreadLocal<String>() {
    @Override
    protected String initialValue() {
        return "This is the initial value";
    }
};
// 特别注意:
// 1. ThreadLocal实例 = 类中的private、static字段
// 2. 只需实例化对象一次 & 不需知道它是被哪个线程实例化
// 3. 每个线程都保持 对其线程局部变量副本 的隐式引用
// 4. 线程消失后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)
// 5. 虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程只能访问到自己通过调用ThreadLocal的set()设置的值
// 即 哪怕2个不同的线程在同一个`ThreadLocal`对象上设置了不同的值,他们仍然无法访问到对方的值
// 1. 设置值:set()
// 需要传入一个Object类型的参数
myThreadLocal.set("初始值”);
// 2. 读取ThreadLocal变量中的值:get()
// 返回一个Object对象
String threadLocalValue = (String) myThreadLocal.get();
实现原理:
TheradLocal 类中有一个map(ThreadLocalMap) :  用于存储每个线程 设置的存储在 ThreadLocal 变量的值
1、ThreadLocalMap的键 key = 当前ThreadLocal 实例、 值value = 该线程设置存储在ThreadLocal变量的值
2、该key 是ThreadLocal对象的弱引用,当要抛弃掉 ThreadLocal对象时,垃圾收集器会忽略该key 的引用二清理掉ThreadLoacl对象。
// ThreadLocal的源码
public class ThreadLocal<T> {
    ...
  /**
    * 设置ThreadLocal变量引用的值
    *  ThreadLocal变量引用 指向 ThreadLocalMap对象,即设置ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
    *  ThreadLocalMap的键Key = 当前ThreadLocal实例
    *  ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
    **/  
    public void set(T value) {
      
        // 1. 获得当前线程
        Thread t = Thread.currentThread();
        // 2. 获取该线程的ThreadLocalMap对象 ->>分析1
        ThreadLocalMap map = getMap(t);
        // 3. 若该线程的ThreadLocalMap对象已存在,则替换该Map里的值;否则创建1个ThreadLocalMap对象
        if (map != null)
            map.set(this, value);// 替换
        else
            createMap(t, value);// 创建->>分析2
    }
  /**
    * 获取ThreadLocal变量里的值
    * 由于ThreadLocal变量引用 指向 ThreadLocalMap对象,即获取ThreadLocalMap对象的值 = 该线程设置的存储在ThreadLocal变量的值
    **/
    public T get() {
        // 1. 获得当前线程
        Thread t = Thread.currentThread();
        // 2. 获取该线程的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        // 3. 若该线程的ThreadLocalMap对象已存在,则直接获取该Map里的值;否则则通过初始化函数创建1个ThreadLocalMap对象
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value; // 直接获取值
        }
        return setInitialValue(); // 初始化
    }
  /**
    * 初始化ThreadLocal的值
    **/
    private T setInitialValue() {
        T value = initialValue();
        // 1. 获得当前线程
        Thread t = Thread.currentThread();
        // 2. 获取该线程的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
         // 3. 若该线程的ThreadLocalMap对象已存在,则直接替换该值;否则则创建
        if (map != null)
            map.set(this, value); // 替换
        else
            createMap(t, value); // 创建->>分析2
        return value;
    }
  /**
    * 分析1:获取当前线程的threadLocals变量引用
    **/
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
  /**
    * 分析2:创建当前线程的ThreadLocalMap对象
    **/
    void createMap(Thread t, T firstValue) {
    // 新创建1个ThreadLocalMap对象 放入到 Thread类的threadLocals变量引用中:
        // a. ThreadLocalMap的键Key = 当前ThreadLocal实例
        // b. ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
        t.threadLocals = new ThreadLocalMap(this, firstValue);
        // 即 threadLocals变量 属于 Thread类中 ->> 分析3
    }
  
    ...
}
  /**
    * 分析3:Thread类 源码分析
    **/
    public class Thread implements Runnable {
       ...
       ThreadLocal.ThreadLocalMap threadLocals = null;
       // 即 Thread类持有threadLocals变量
       // 线程类实例化后,每个线程对象拥有独立的threadLocals变量变量
       // threadLocals变量在 ThreadLocal对象中 通过set() 或 get()进行操作
       ...
}
问题: ThreadLoacl 如何做到现成安全的
  • 每个线程拥有自己独立的Threadlocals变量,也就是ThreadLocalMap
  • 每当线程访问ThreadLocals变量时,访问的都是各自线程自己的 ThreadLocalMap变量(键值对)
  • ThreadLocalMap变量的键key = 唯一 = ThreadLocal 实例
问题:TheadLocal 与同步机制的区别
线程池 ThreadPool
定义: 一块缓存了一定现成数量的区域
作用:复用线程  管理线程(统一分配,监控,  控制线程池最大并发数)
优点:降低因线程的创建 销毁所带来的性能开销 - 可以重用缓存在线程池的线程。提高线程响应速度,执行效率。提高线程管力度
线程池有六个核心参数
// 创建线程池对象如下
// 通过 构造方法 配置核心参数
   Executor executor = new ThreadPoolExecutor(
                                              CORE_POOL_SIZE,
                                              MAXIMUM_POOL_SIZE,
                                              KEEP_ALIVE,
                                              TimeUnit.SECONDS,
                                              sPoolWorkQueue,
                                              sThreadFactory
                                               );
// 构造函数源码分析
    public ThreadPoolExecutor (int corePoolSize,
                               int maximumPoolSize,
                               long keepAliveTime,
                               TimeUnit unit,
                               BlockingQueue<Runnable workQueue>,
                               ThreadFactory threadFactory )
——————————————————————————————————————————————————————————————————————————————
// 1. 创建线程池
   // 创建时,通过配置线程池的参数,从而实现自己所需的线程池
   Executor threadPool = new ThreadPoolExecutor(
                                              CORE_POOL_SIZE,
                                              MAXIMUM_POOL_SIZE,
                                              KEEP_ALIVE,
                                              TimeUnit.SECONDS,
                                              sPoolWorkQueue,
                                              sThreadFactory
                                              );
    // 注:在Java中,已内置4种常见线程池,下面会详细说明
// 2. 向线程池提交任务:execute()
    // 说明:传入 Runnable对象
       threadPool.execute(new Runnable() {
            @Override
            public void run() {
                ... // 线程执行任务
            }
        });
// 3. 关闭线程池shutdown()
  threadPool.shutdown();
  
  // 关闭线程的原理
  // a. 遍历线程池中的所有工作线程
  // b. 逐个调用线程的interrupt()中断线程(注:无法响应中断的任务可能永远无法终止)
  // 也可调用shutdownNow()关闭线程:threadPool.shutdownNow()
  // 二者区别:
  // shutdown:设置 线程池的状态 为 SHUTDOWN,然后中断所有没有正在执行任务的线程
  // shutdownNow:设置 线程池的状态 为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
  // 使用建议:一般调用shutdown()关闭线程池;若任务不一定要执行完,则调用shutdownNow()
根据不同参数的不同配置,java中最常见的线程池有四种
  • 定长线程池(FixedThreadPool)
  • 定时线程池(ScheduledThreadPool )
  • 可缓存线程池(CachedThreadPool)
  • 单线程化线程池(SingleThreadExecutor)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值