要了解Java并发就要从以下几个方面去展开:
1. 什么是进程?
2. 什么是线程?
3. 什么是多线程?
4. 多线程的问题?
1. 进程
进程出现的意义:
脱离了与业务的关联,转化为计算机体系的概念,作为一个实例,可以进一步细化、分化。也就是说:当进程出现,平常意义的一个 “程序” 转化为一个“具有多种状态”、“利用系统资源”、“可与其他同样实例共同竞争CPU” 从而“实现处理业务”的 “任务活动”。
随着操作系统的并发和共享性展现,进程的概念浮出水面。
- 并发:计算机系统中同时存在多个运行中的程序,操作系统具有同时处理和调度多个程序同时执行的能力。
- 共享:系统中的资源可供内存中多个并发执行的进程同时使用。
如何理解程序和进程的不同?
program: a plan for the programming of a mechainsm (as a computer)
程序:为了实现某种机制的程序设计(简译)
process: a series of things that are done in order to achieve a particular result
进程:为了实现某种机制而执行的一系列动作
因为操作系统的并发和共享性,使用“程序”来作为系统的调度单位已经不符合实际,而进程所展现的间断性、共享性更符合操作系统工作过程。
更重要的是,进程更符合系统工作过程中动态性、过程性的实体运作过程。
就像工厂中我们一般叫做流水线,而不叫其他与加工、制造更贴切的名称(瞎编的)
当然不是名称上的改变而已,进程已经是和程序不同的两个概念。有着不同的特征。
2. 线程——为优化而生
进程可以理解为一个程序的一次动态执行过程,经过创建、运行、阻塞、销毁等状态变化完成了一次功能的实现。
那为什么还需要线程的出现?
个人理解:拥有资源和被调度二者不可兼得,尤其是拥有庞大资源的单位,进程是拥有资源的基本单位,这意味着进程本身是一个重量级的组件,如果同时作为被CPU调度的基本单位,会对调度过程产生影响,因此从资源单位分化出调度单位很有必要。
引出几个概念:
进程是拥有资源的基本单位,线程不拥有系统资源,但线程可以访问隶属进程的系统资源,同一进程的各个线程共享该进程的所有资源。
线程是调度的基本单位。
3. 多线程——进程实现的功能可以继续划分多个子功能
并发场景。
4. 多线程的问题
铺垫了这么多,终于到了重点位置。
总结:多线程为什么会有问题?
(1)顺序问题:功能决定顺序,执行不保证顺序导致出现问题
i = a+b*c , 代码本身就决定了它具有顺序,先执行乘法,后执行加法,我开启两个线程来实现
t1(a,b)实现加法,t2(a,b)实现乘法,但是因为线程的调度策略(时间片轮换)不保证顺序,无法保证 t2 在 t1之前实现。
(2)原子问题:功能决定原子问题,执行不保证原子问题。 i++,代码本身决定它是一个i = i+1 的原子操作,但是多线程下(线程调度的时机不确定),可能在取到i之后,CPU开始调度其他线程,导致原子问题
(3)java 本身的问题:Java的主内存与工作内存,可以理解为因为加了缓存导致的不一致问题。线程1改变某个值在线程1的工作内存,线程2也对该值处理,但是被线程1修改后的值因为没有同步到主内存导致线程2处理时没拿到出现问题。(可见性问题)
详细来讲:
(1)顺序问题
常见的重排序会导致多线程安全问题。为了优化执行,执行器会采用乱序执行的过程,在单线程的场景下是一种有效的优化手段,但是在多线程下会导致问题。
解决方案:volatile
(2)原子问题
这里又牵扯到读写问题,一个资源如果可以被共享,又可以细分为:
共读、共写。
解决方案:加锁
(3)缓存所带来的数据不一致问题
可见性问题。