《码出高效》第七章

31 Oct 2021

Reading time ~10 minutes

并发是指在某个时间段内,多任务交替处理的能力。

并行是指同时处理多任务的能力。

它们的核心区别在于进程是否同时执行。

在并发环境下,由于程序的封闭性被打破,出现以下特点:

  1. 并发程序之间有相互制约的关系
  2. 并发程序的执行过程是断断续续的
  3. 当并发数量设置合理并且CPU拥有足够的处理能力时,并发会提高程序的运行效率

7.1 线程安全

线程在生命周期内的状态有:

  • NEW,新建状态,是线程被创建且未启动的状态
  • RUNNABLE,就绪状态,是调用start()之后运行之前的状态
  • RUNNING,运行状态,是run()正在执行时线程的状态
  • BLOCKED,阻塞状态,进入此状态,有以下情况:
    • 同步阻塞:锁被其他线程占用
    • 主动阻塞:调用Thread的某些方法,主动让出CPU执行权
    • 等待阻塞:执行了wait()
  • DEAD,终止状态,是run()执行结束,或因异常退出后的状态,此状态不可逆转

保证高并发场景下的线程安全,可以从以下四个维度衡量:

  • 数据单线程内可见
  • 只读对象
  • 线程安全内
  • 同步与锁机制

并发包主要分成以下几个类族:

  • 线程同步类
  • 并发集合类
  • 线程管理类
  • 锁相关类

7.2 什么是锁

Java中常用锁实现的方式有两种:

1、用并发包中的锁类

2、利用同步代码块

synchronized提供三种锁的实现,包括偏向锁、轻量级锁、重量级锁

7.3 线程同步

7.3.1、同步是什么

原子性是指不可分割的一系列操作指令,在执行完毕前不会被任何其他操作中断,要么全部执行,要么全部不执行。

7.3.2 volatile

happen before

指令优化

可见性是指某线程修改共享变量的指令对其他线程来说都是可见的,它反映的是指令执行的实时透明度。

当使用volatile修饰变量时,意味着任何对此变量的操作都会在内存中进行,不会产生副本,以保证共享变量的可见性,局部阻止了执行重排的发生。

7.3.3 信号量同步

信号量同步是指在不同的线程之间,通过传递同步信号量来协调线程执行的先后次序。

7.4 线程池

7.4.1 线程池的好处

线程的创建需要开辟虚拟机栈、本地方法栈、程序计数器等线程私有的内存空间,在线程销毁时需要回收这些系统资源。频繁地创建和销毁线程会浪费大量的系统资源,增加并发编程的风险。

线程池的好处:

  • 利用线程池管理并复用线程、控制最大并发数等
  • 实现任务线程队列缓存策略和拒绝机制
  • 实现某些与时间相关的功能
  • 隔离线程环境

7.4.2 线程池源码详解

总结一下,使用线程池要注意以下几点:

  • 合理设置各类参数,应根据实际业务场景来设置合理的工作线程数
  • 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程
  • 创建线程或线程池时请执行有意义的线程名称,方便出错时回溯

7.5 ThreadLocal

7.5.1 引用类型

引用类型分为以下四种:

  • 强引用:只要对象有强引用指向,并且GC Roots可达,那么Java内存回收时,即使濒临内存耗尽,也不会回收该对象
  • 软引用:在即将OOM之前,垃圾回收器会把这些软引用指向的对象加入回收范围,以获得更多的内存空间,让程序能够继续健康运行
  • 弱引用:如果弱引用执行的对象只存在弱引用这一条线路,则在下一次YGC时会被回收
  • 虚引用:定义完成后,就无法通过该引用获取指向的对象

7.5.2 ThreadLocal价值

7.5.3 ThreadLocal副作用

ThreadLocal的主要问题是会产生脏数据和内存泄漏。

解决办法很简单,在每次用完ThreadLocal时,必须要及时调用remove()方法清理。



Reading NotesEasy Coding Share Tweet +1