并发是指在某个时间段内,多任务交替处理的能力。
并行是指同时处理多任务的能力。
它们的核心区别在于进程是否同时执行。
在并发环境下,由于程序的封闭性被打破,出现以下特点:
- 并发程序之间有相互制约的关系
- 并发程序的执行过程是断断续续的
- 当并发数量设置合理并且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()方法清理。