提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
java多线程并发编程本质上就是为了充分使用CPU从而提高程序执行效率。为了避免多线程并发环境下数据一致性问题,并根据并发编程特性提供一系列实现解决这个问题。
并发三大特性:原子性、可见性、有序性;锁概念和锁实现;volatile和synchronization;重量锁和轻量锁以及锁升级;CAS和AQS。
sunchronized锁升级流程图、CAS流程图、AQS流程图。

进程是操作系统资源分配的基本单位。
例如在操作系统中,存在各种可执行的程序文件,当我们启动程序时,操作系统会在内存中为程序运行分配内存空间,这个正在执行的程序就是一个进程。一个操作系统可以同时启动多个程序,也就是可以同时运行多个进程。
线程是操作系统调度单位的最小单位。
在一个进程中可以创建多条线程,例如一个可以同时执行多个任务。多条线程共享同一进程下的资源(这就是为什么会有多线程并发问题)。

串行:同一时间只能做一件事情,需要等前面事情处理完了再执行下一个。
并行:同一时间多个事务同时进行。
并发:在程序执行中,多个事务交替执行。
注:
java多线程并发编程本质上就是为了充分使用CPU从而提高程序执行效率。为了避免多线程并发环境下数据一致性问题,并根据并发编程特性提供一系列实现解决这个问题。
a. 继承Thread类,重写run()方法后调用start()方法开启线程。
b. 实现Runnable接口,实现run()方法,新建一个Thread将Runnable作为构造参数传入比调用start()方法启动线程。
c. 实现Callable接口,实现call()方法。
d. 使用线程池方式获取线程对象。
Thread和Runnable的区别:一个是继承,一个是接口,从对象特性看使用接口实现的更为灵活。
Runnable和Callable的区别:call()方法有返回值,run()方法没有返回值。
在操作系统中,线程只有运行、阻塞和结束三个状态。在java环境中,java为线程管理又多了新建、就绪、wait、sleep、block等状态。
a. 新建:new Thread()。
b. 就绪:调用 start()方法。
c. 运行:线程获取到cpu资源执行。
d. 阻塞:cpu线程切换、锁等待、线程调用wait()sleep()方法。
e. 结束:线程执行完毕或者调用interrupt()stop()等方法。
理解并发编程的有序性,需要先理解Happens-Before原则和指令重排。
在java环境中,需要一个规则来确定操作与操作之间的先后执行顺序,这个判断规则就是Happens-Before规则。分别有以下几条规则:
java属于高级语言,在jvm中会进一步解析编译为操作系统可执行的编程语言。在java语言中一行代码会解析为多条指令,同时jvm为了提高执行效率,会在符合Happens-Before原则下对这些指令进行重排序。
例如:java的半初始化状态,会对成员变量的赋值拆分为三步:开辟内存空间并设置默认值、对内存空间赋值引用、对内存空间设置真实值。
先了解线程不可见性再理解为什么会需要可见性。如图,当线程操作共享资源时,会先从共享内存空间拷贝数据到线程临时空间,这个临时空间是线程独享的,其它线程不可见。同时,每个线程内部的操作过程对另一个线程来说也是不可见的。

由于线程直接的不可见性,为了保证多线程并发环境下数据安全,需要保证共享数据在多线程操作中的可见性。
在java中,提供了volatile和synchronized保证数据可见性。
在java环境中,使用了锁来实现原子性。也就是加了锁的操作(属性赋值、代码块)会作为一个整体执行。
JVM提供了四个汇编指令级别内存屏障来保证内存数据读写的原子性。
LoadLoad、LoadStore、StoreStore、StoreLoad
使用volatile修饰的变量保证了线程可见性和禁止指令重排。
java提供synchronizated实现对方法或者代码块的加锁动作。
重量级锁:早期JDK版本的synchronized
轻量级锁:偏向锁、自旋锁
如图,java的synchronized升级经历了偏向锁-轻量级锁(自旋锁)-重量级锁的过程。

CAS 是单词 complete and save缩写,是乐观锁的一种实现。具体流程图如下,

实现CAS有两点需要注意:
AQS是AbstractQueueSynchronizer缩写。AQS底层就是volatile和CAS的实现。
如下图,AQS主要内存结构:

