前边其实我们已经梳理总结了多线程相关知识:Java高并发——多线程 ,这篇,我们再从JVM的角度,来看看高效并发。
JAVA内存模型:
1,在前边我们学习Java内存区域管理中,像程序计数器、栈为线程独享的,堆和方法区为线程共享的。在JVM中对于对线程,也分为工作内存和主内存,其实也可以简单理解为上边两种。看下模型图:
2,看下上边重要的工作内存和主内存的交互:Java内存模型中定义了8中操作来进行完成,Jvm保证每一种操作都为原子性。如下:
名称 | 描述 |
---|---|
lock(锁定) | 作用于主内存的变量,它把一个变量标识位一条线程独占的状态。 |
unlock(解锁) | 作用于主内存的变量,他把一个处于锁状态的变量释放出来,释放后的变量才能被其它线程锁定。 |
read(读取) | 作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,供后边的load动作使用。 |
load(载入) | 作用于工作内存的变量,它把read操作得到的变量值放入工作内存的变量副本中。 |
use(使用) | 作用于工作内存的变量,它把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时执行操作操作。 |
assign(赋值) | 作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行此操作。 |
store(存储) | 作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便write操作使用。 |
write(写入) | 作用于主内存的变量,它将store操作后的变量的值放入内存的变量中。 |
上边这八中操作是不是很容易理解,lock,read,load,use,assign,store,write,unlock是不是顺序完成了程序从锁定、读取数据、使用数据、变更数据、更新写入数据,解锁的完全过程。
但是这八种操作也有其使用的规则:1,不允许read和load、store和write操作之一单独出现;2,不允许一个线程丢弃它的最近的assign操作;3,不允许一个线程无原因(没有任务assign)把数据同步回主内存;4,新的变量必须在主内存中诞生;5,一个变量同一时刻只能被一个线程进行lock操作;6,没有进行lock是不允许进行unlock的;7,进行unlock之前,必须将变量同步回主内存中;8,执行lock操作,将会清空工作内存此变量的值,需要重新执行load或assign操作。好,这里再回顾下前边学习的,Happen-Before原则:高并发-了解并行的世界
3,volatile型变量的特殊规则:
1,线程对volatile修饰变量,执行read、load和use必须时相互关联,连续出现;(保证每次使用都是拿的主内存中最新的值) |
2,线程对volatile修饰变量,执行assign和store、write时相互关联,连续出现;(保证每次修改后必须立刻同步回主内存中) |
3,处理volatile修饰变量时,这八种指令操作,不会进行指令重排,严格按照先后顺序进行执行。 |
满足上边这三个规则,是不是,变量被volatile修饰后,其针对所有线程的可见性特性是不是就很容易理解。
4,对于long和double型变量的特殊规则:上边8种操作对64位的long和double,分两次32位的操作进行,即不保证其原子性。
综上可以看出,java线程模型中八个操作,通过上边的规则(也包括Happen-Before原则),即可到达原子性、可见性、有序性。能够更加容易帮助我们理解多线程。
好,在JVM层面,在多线程上还帮我们优化有关锁的性能,这里前边提到过,看下即可:Lock优化,提高性能。
了解JVM,掌握多线程,编写优质代码,加油!!!