多线程

多线程

进程和线程的区别

多进程 多线程
稳定性 多进程程序独占一个进程的地址空间,程序更加稳定,一个进程的崩溃不会导致整个进程的崩溃 多线程程序的健壮性差于多线程,线程的崩溃往往会导致整个程序的崩溃
同步 采用信号机制,比多线程简单 因为多线程程序公用同一个进程的地址空间,因此对于全局变量的访问需要进行控制,比如加锁,加锁与锁的撤销是很耗费系统资源的,影响程序的效率
通信 多进程程序之间的通信需要采用特殊的IPC机制(比如匿名管道,命名管道,消息队列,共享内存,信号等) 同样因为使用同一个进程的地址空间,使得线程之间的通信更加灵活与方便
切换 进程切换,采用中断的机制,中断当前进程,需要保存进程上下文进程信息,涉及到页表切换 线程只需要保存线程的上下文就好
消耗 进程的创建于撤销要更加耗费资源,每个进程都要分配独立的虚拟地址空间 线程共用同一个进程的同一块地址空间,不需要单独向操作系统申请,一次效率更高
  • 进程通信(管管消信共):匿名管道,命名管道,消息队列,信号量,共享内存。
  • 线程通信:wait/notify机制;volatile修饰的全局变量;消息队列;事件。
  • 线程同步:互斥量(同步不同进程的线程),临界区(同步进程内的线程),信号量,事件。

死锁

  • 必要条件:互斥,不可抢占,请求与保持,循环等待。
  • 死锁预防:
    • 破坏“不可抢占”条件
      • 持有资源的线程请求新资源的请求没有满足,释放掉已经持有的资源。
    • 破坏“请求与保持”条件
      • 一次性申请所有的资源。
      • 申请初期运行所需的资源,运行过程中逐步释放。
    • 破坏“循环等待”条件
      • 资源编号,持有编号为i的线程只能申请编号大于i的资源。
  • 死锁避免:利用额外的校验信息判断,不出现死锁时才分配资源。
  • 死锁解除:
    • 抢占资源分配给死锁进程,解除死锁状态。
    • 终止或撤销死锁进程,直至解除死锁状态。

Synchronized

  • JVM层面的锁。
  • 对象:对象头(Mark Word,Class Point,Monitor),实例信息,对齐填充。
  • 同步方法:读取ACC_SYNCHRONIZED标志位实现。
  • 同步代码块:monitorenter,monitorexit。
  • 偏向锁:一个线程常常获得同一个锁。轻量级锁:线程少,持有锁的时间短,自旋等待锁释放。重量锁:没有持有锁的线程阻塞,防止空转。
  • 锁优化:锁的升级,偏向锁(不会主动释放)比较对象头和线程的threadID检查线程存活升级,轻量级锁自旋升级,重量级锁阻塞没有锁的线程,防止CPU空转;锁粗化,避免频繁加锁;锁消除,逃逸分析去除没必要的锁。
  • 类锁:修饰静态方法或类(class)。对象锁:修饰方法或代码块(this)。两者不冲突。
  • Integer作为锁对象:不行
    • Integer++创建新的Integer对象,并将引用赋值给Integer。
    • 导致对不同的对象实例加锁,临界区控制出现问题。

Lock

  • ReenTrantReadWriteLock:

    • ReadLock的获取需要当前线程持有读(重入)/写(再次获取)锁,其他线程没有没有写锁;
    • WriteLock的获取需要当前线程持有写锁,没有锁且CAS成功。写锁的个数为0才能认为锁释放成功。读写锁适用于读多写少的场景。
    • 锁降级:获取写锁,获取读锁,释放写锁。写锁降级为读锁。
  • ReentrantLock

    • API层面的互斥锁。
    • 可以实现公平锁。非公平锁第一次CAS抢锁失败后,会进入AQS同步队列排序,当位于队首时会和其他线程开始抢锁。
    • 通过 lock.lockInterruptibly() 来实现能够中断等待锁线程的机制。
    • 可以同时绑定多个Condition对象。
  • AQS

    • 提供了一种阻塞锁和依赖FIFO等待队列同步器的框架。
    • 核心机制基于CLH队列(不存在队列实例,仅存在节点关系)实现,将请求资源的线程封装成CLH队列的一个Node来实现锁的分配。
    • 实现共享资源state的获取和释放方式(acquire独享,acquireShared共享)定义独享(ReentrantLock)和共享(Semaphore)。

    img

    • 一个锁对应一个阻塞队列和多个条件队列。
    • 多个线程调用锁的时候,其中一个线程获取到锁,其他线程丢到阻塞队列中。
    • 获取到锁的线程调用条件变量的await方法后,线程转换为Node进入条件队列,阻塞队列中的线程获取锁。
    • 线程调用signal方法后,条件队列中的Node移动到阻塞队列中,调用unpark方法授权,等待获取锁。
  • CAS:用CPU指令实现无锁的数据操作,提高效率。缺点:CAS长时间不成功会增加CPU开销;只能保证一个共享变量的原子操作,多个变量操作需要用到锁;ABA(AtomicStampedReference标志解决)。

  • StampedLock:乐观锁,相对于读写锁的改进在于,读的过程中允许线程获取写锁后写入。为了避免读取不一致的情况,需要判断读的过程中是否有写入。

ThreadLocal

  • 指定线程存储数据,维护自己的变量副本/拷贝

    • set/get方法,获取当前线程的ThreadLocalMap对象,key为ThreadLocal,value对应存储的值。
  • 内存泄漏:ThreadLocalMap存在Key弱引用ThreadLocal的Entry,threadlocal被回收后,出现null Key无法找到value。解决方法:每次使用完ThreadLocalMap,调用threadlocal的remove方法。

    ThreadLocal

  • 解决的问题:适用变量在线程中隔离,在方法中共享的场景;减少了临界区的范围;减少了线程的切换。

  • Spring对Bean(singleton作用域)中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

volatile

  • MESI(缓存一致性协议):其他CPU存在变量副本时缓存行置为无效状态;锁bus;总线风暴,嗅探机制与CAS不断失败交互(部分使用Synchronized)。
  • 可见性:线程修改变量值能立即同步到主内存,每次使用前从主内存中刷新。嗅探机制检查缓存是否过期,缓存行对应的内存地址修改则强制失效。
  • 有序性:禁止指令重排序;happens-before(volatile变量的写happens-before读);as-if-serial(单线程执行结果不变)。
  • 内存屏障:阻止屏障两侧的指令重排序;将脏数据写回主内存,让缓存中相应的数据失效。

线程池

img

  • 当有新任务来的时候,先看看当前的线程数有没有超过核心线程数,如果没超过就直接新建一个线程来执行新的任务,如果超过了就看看缓存队列有没有满,没满就将新任务放进缓存队列中,满了就新建一个线程来执行新的任务,如果线程池中的线程数已经达到了指定的最大线程数了,那就根据相应的策略拒绝任务。
  • 当缓存队列中的任务都执行完了的时候,线程池中的线程数如果大于核心线程数,就销毁多出来的线程,直到线程池中的线程数等于核心线程数。此时这些线程就不会被销毁了,它们一直处于阻塞状态,等待新的任务到来。
  • 种类:newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor。
  • 主要参数:
    • 核心线程数:线程进入工作队列,工作队列满时创建超过这个数量的线程;
    • 最大线程数空闲时间:线程数大于核心线程数时,未执行任务达到空闲时间则终止;
    • 缓冲队列:LinkedBlockingQueue;ArrayBlockingQueue;SynchronousQueue(生产者线程对其的插入操作put必须等待消费者的移除操作take)。
    • 工厂方法:指定不同线程池类型;
    • 拒绝策略:缓存队列已满,线程达到上限。抛出异常,丢弃或丢弃最早的,重试;
    • Hash表:维护线程的引用;
    • submit:使用future获取任务的执行结果。
  • JUC
    • tools:工具类;
      • CountDownLatch,CyclicBarrier,Semaphore
    • executor:执行线程的工具;
      • Callable,Future,Executor
    • atomic:原子性包;
    • locks:锁包;
      • ReentrantLock,ReadWriteLock
    • collections:线程安全的集合。
      • Queue,ConcurrentHashMap
  • 线程状态:新建,就绪,运行,阻塞,死亡。
  • 获取线程状态:Thread.getState()

CyclicBarrier和CountDownLatch

  • CyclicBarrier:循环栅栏,调用await()加1未达到目标值时,计数器为阈值时置0.
  • CountDownLatch:原子计数器,调用CountDownLatch对象的countDown方法计数器为0时才执行await方法。
posted @ 2020-08-09 08:59  樱空废宅  阅读(104)  评论(0)    收藏  举报