2.2 多线程
2.2.1 锁
- Java对象头和Monitor
    - 对象在内存分为, 对象头、实例数据和对齐填充
- 对象头实现synchronized, 轻量锁和重量锁在Mark Word结构中定义
- ObjectMonitor,- wait状态的线程加入- _WaitSet, 等待锁- block状态的线程加入- _EntryList
- monitor对象在于每个对象的Java头, 因此Java任意对象都可以作为锁
 
- synchronized代码块底层原理
    - monitorenter指令, 进入同步方法,- monitorenter则通过操作系统的互斥锁实现
- monitorexit指令, 退出同步方法
 
- synchronized方法底层原理
    - 隐式, 无需通过monitorenter和monitorexit字节码指令控制
- 方法表结构中ACC_SYNCHRONIZED标识
 
- 隐式, 无需通过
- Java虚拟机synchronized的优化
    - 偏向锁
        - 线程A获得锁, 锁即进入偏向模式, Mark Word变为偏向锁结构, 线程A再次请求锁, 无需操作即获得锁
 
- 线程A获得锁, 锁即进入偏向模式, 
- 轻量级锁
        - 线程交替执行同步块的场景
 
- 自旋锁
        - 避免用户态和内核态的切换
 
- 锁消除
        - 优化没必要的锁
 
 
- 偏向锁
        
- synchronized的可重入性
    - 对相同对象请求锁, 是允许的, 即synchronized的可重入性
 
- 线程中断和synchronized
    - 线程中断操作对synchronized不起作用
 
- 等待唤醒机制与synchronized
    - notify(),- notifyAll(),- wait()方法必须在- synchronized代码块内或者方法中
 
2.2.2 CAS (Compare And Swap)[V, E, N]
- CPU指令对CAS的支持
    - CAS是CPU的原子指令
 
- UnSafe指针
    - Unsafe#compareAndSwapObject()
- Unsafe#compareAndSwapInt()
- Unsafe#compareAndSwapLong()
 
- 并发中的Atomic
    - 原子更新基本类型
        - AtomicBoolean
- AtomicInteger
- AtomicLong
 
- 原子更新引用
- 原子更新数组
- 原子更新属性
- 自旋锁
 
- 原子更新基本类型
        
2.2.3 wait/notify、sleep/
- Object#wait()通过监视锁对象完成, 需要先获得- monitor的所有权, 通过- synchronized关键字完成,- Object#notify()同理
- wait和- sleep的区别- sleep不影响锁的状态,- wait释放锁
- 调用wait之后需要调用notify来获取CPU资源
- yield让CPU调度, 类似- sleep只是不能指定时间
- join, Thread A调用Thread B- #join, A需要等B结束才能执行
 
2.2.4 ThreadLocal
- ThreadLocal存放到ThreadLocalMap
2.2.5 线程池实现原理 Executor
- ThreadPoolExecutor
    - corePoolSize 核心线程数
- maximumPoolSize 维护最大线程数
- keepAliveTime 空闲线程存活时间
- workQueue 任务队列
- threadFactory 线程工厂
- handler 拒绝策略
 
- 创建规则(corePoolSize最小存活线程数量[除非allowCoreThreadTimeOut为true])
    - 
        - 线程数量小于corePoolSize, 创建线程处理任务
 
- 
        - 线程大于corePoolSize并小于maximumPoolSize之间, workQueue未满, 缓存新任务
 
- 
        - 线程小于maximumPoolSize且workQueue满了, 创建新线程处理任务
 
- 
        - 线程大于maximumPoolSize且workQueue满了, 使用拒绝策略
 
 
- 
        
- 资源回收
- 排队策略
- 拒绝策略
    - AbortPolicy (默认, 直接抛异常)
- CallerRunsPolicy (只用调用者所在的线程执行任务)
- DiscardOldestPolicy (丢弃任务队列中最久的任务)
- DiscardPolicy (丢弃当前任务)
 
- Executors
    - ThreadPoolExecutor
        - Executors#newFixedThreadPool(int nThreads) -> new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());- corePoolSize&maximumPoolSize数量均为nThreads
- keepAliveTime为0, 即空闲即停止
- LinkedBlockingQueue无界队列, 即不拒绝任务
 
- Executors#newCachedThreadPool() -> new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>()), 对短时间任务性能较好- corePoolSize为0, maximumPoolSize为无限大
- keepAliveTime为60S
- SynchronousQueue, 无存储空间, 无空闲线程则创建新线程
 
- Executors#newSingleThreadExecutor() -> new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue)- corePoolSize&maximumPoolSize数量均为1, 即创建一条线程处理任务
- LinkedBlockingQueue无界限队列任务
 
- Executors#newScheduledThreadPool(int corePoolSize)- corePoolSize核心线程数
 
 
- ScheduledThreadPoolExecutor
        - ScheduledThreadPoolExecutor#scheduleWithFixedDelay, 固定延迟执行
- ScheduledThreadPoolExecutor#scheduleAtFixedRate, 固定频率执行
 
 
- ThreadPoolExecutor
        
2.2.6 Lock & ReentrantLock
- Lock与- synchronized的区别- Lock提供可中断获取, 超时中断获取
- Lock提供等待唤醒机制的多条件变量- Condition
 
- ReentrantLock(重入锁)
    - 使用:
 ReentrantLock reentrantLock = new ReentrantLock(); try { reentrantLock.lock(); } finally { reentrantLock.unlock();// lock几次, 就要unlock几次 }- 原理
        - AbstractQueuedSynchronizer,包含- Node head同步队列头,- Node tail同步队尾,- int state0表示锁未被占用, 1表示锁被占用,- Node, 包含- Node SHARED = new Node()共享模式,- Node EXCLUSIVE = new Node()独占模式,- AbstractQueuedSynchronizer提供共享和独占模板给子类实现, 包含- tryAcquire()和- tryAcquireShared()
- Sync,- ReentrantLock的内部类, 继承- AbstractQueuedSynchronizer
- NonfairSync,- ReentrantLock的内部类, 非公平锁
- FairSync, 公平锁
- ReentrantLock#lock()->- NonfairSync#lock()- -> compareAndSetState(0, 1)执行CAS操作, 获取同步状态, ->setExclusiveOwnerThread(Thread.currentThread)设置独占线程为当前线程
- else -> acquire(1)- -> tryAcquire()- -> nonfairTryAcquire()- compareAndSetState()再次获取同步状态
- current == getExclusiveOwnerThread()判断当前线程是否是- ownerThread, 重入锁
 
 
- -> 
- -> acquireQueued(addWaiter(Node#EXCLUSIVE))- Node#addWaiter()->- Node#enq(), 将新节点增加到队尾
- acquireQueued()-> 自旋过程, 满足条件- p == head && tryAcquire()并获取同步状态
 
 
- -> 
- Condition包含- await(),- signal()
 
- -> 
 
 
- Semaphore, 共享锁- 
        使用: Semaphore semp = new Semaphore(5); try { semp.acquire(); } finally { semp.release(); }
 
- 
        
- 读写锁, 读锁不互斥, 写锁与其它均互斥
- 自旋锁, 未获得锁则不断重试直到拿到锁
2.2.7 volatile
- Java内存区域
    - 方法区
- 虚拟机栈
- 本地方法栈
- 堆
- 程序计数器
 
- Java内存模型
    - 原子性
- 重排序
        - 编译器优化重排序, 不改变单线程的语义下, 重排语句的执行顺序
- 指令并行重排
- 内存系统的重排
 
- 可见性, 在多线程环境下, 重排导致程序轮序执行的问题, 导致可见性问题
- 有序性
  synchronized可解决原子性, 可见性, 有序性问题,volatile可解决可见性, 有序性问题
 
- JMM的happens-before原则[辅助保证程序执行的原子性、可见性以及有序性]
- volatile内存语义
    - 保证volatile修饰的变量修改的新值对所有线程可见
- 禁止指令重排序
 
2.2.8 BlockQueue实现原理
- ArrayBlockingQueue通过- ReentrantLock和- Condition实现
2.2.9 线程的状态
- New, 新建状态, 还未调用start()时
- Ready, 就绪状态, 调用start()之后或者调用yield
- Running, 运行状态, 获得CPU资源
- BLOCKED, 阻塞状态, 等待锁, 进入synchronized方法或者synchronized块
- TIMED Waiting, 超时等待, 调用sleep(long),join(long),wait(long)
- Waiting, 等待状态, 调用wait(),join()
- Terminated, 结束状态
图片来自知乎Dawell的答案:

2.2.10 AsyncTask
- 线程池
// 串行线程池, 处理任务入队
// Executor sDefaultExecutor = new SerialExecutor();默认执行时调用
private static class SerialExecutor implements Executor {
        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
  public static final Executor THREAD_POOL_EXECUTOR;
  // CORE_POOL_SIZE根据CPU核数创建
  // runnable执行线程池
  ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                  CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                  sPoolWorkQueue, sThreadFactory);
          threadPoolExecutor.allowCoreThreadTimeOut(true);
          THREAD_POOL_EXECUTOR = threadPoolExecutor;
  // 执行任务
  public final AsyncTask<Params, Progress, Result> execute(Params... params) {
      return executeOnExecutor(sDefaultExecutor, params);
  }
  public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
      // 执行任务
      exec.execute(mFuture);
  }
线程
- 线程和进程的区别
    - 进程是运行中的程序,线程是进程的内部的一个执行序列
- 进程是资源分配的单元,线程是执行行单元
- 进程间切换代价大,线程间切换代价小
- 进程拥有资源多,线程拥有资源少 多个线程共享进程的资源
 
- 内核线程
    - 内核线程就是内核得分身, 一个分身可以处理一个特定事情。这在处理异步事件如异步IO时特别有用。内核线程得使用是廉价的, 唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间, 支持多线程的内核叫做多线程内核(MultiThreads kernel)
 
- 轻量级线程
    - 轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程, 它是内核线程的高度抽象, 每一个轻量级进程都有一个特定的内核线程关联, 内核线程只能由内核管理并像普通进程一样被调度
 
- 用户线程
    - 用户线程是完全建立在用户空间的线程库, 用户线程的创建、调度、同步和销毁全由库函数在用户空间完成, 不需要内核的帮助, 因此这种线程是极其低消耗和高效的
 
