JVM 虚拟机 内存模型 以及 关键字 volatile 和 锁 简介

JVM 内存模型

看图 :

在这里插入图片描述

8个内存模型的基本操作指令

在主内存和工作内存之间通过8个操作来完成交互 :
lock (锁定): 作用主内存中的变量,标记其为线程独占,即锁定;
unlock (解锁): 作用主内存中的变量,把一个锁定的变量释放,该变量可被其他线程锁定;
read (读取): 作用主内存中的变量,把变量从主内存传输到线程的工作内存中;
load (载入): 载入,把read出的主内存的值,载入到工作内存中;一定与read一同出现;
use (使用): 作用与工作内存的变量,把变量传递给执行引擎,执行计算/赋值等操作;
assign (赋值): 作用于工作内存的变量,把执行引擎完成操作后的结果,赋值给工作内存的变量;
store (存储): 作用于工作内存的变量,把变量值传送到主内存;
write (写入): 写入,把store出工作内存的值,回写入主内存中;一定与store一同出现;
store+write 刚好就是 read+load 的反向操作;

内存模型操作的特性:原子性、可见性、有序性

原子性:基本上我们认为java内存模型中已经保证对一个变量操作的原子性,操作包括:read、load、use、assign、store、write(除了long和double,但是这种特殊问题也不用太过担心,可以忽略)
可见性:volatile对象的每一次操作都会立即刷新主内存,对其他线程可见;
有序性:通过提供volatile防止指令重排序,和synchronized来指定同意时间只允许一个线程对同一个对象执行lock操作,来保证程序执行的有序性;

先行发生原则

两个操作互不影响时,时允许指令重排序的,但是,当a操作的结果时被b操作使用的,那么a操作一定先行发生于b,a和b时不允许对调顺序的;

volatile

声明了volatile的对象,保证了此变量对所有线程可见(可见性),并且时立即可见,也就是每一个线程对该对象执行use时都要从主内存中获取最新的对象值,当某个线程修改(assign)了该变量时,一定会同时通知到所有读取并载入了该对象的线程,你们获取的值已经失效;
但是并不代表volatile就是线程安全的,当多线程同时修改该对象时,还是有可能出现把非正确的值传回主内存的情况的;这个时候还是需要使用到synchronized或者concurrent保中的原子类,来保证对volatile对象操作的原子性。
使用volatile的第二个意义,是防止了指令重排序;(JDK1.5以后才有的特性)
比如:

int a = 1;
int b = 0;
int c = 5;

再虚拟机执行时,很可能时先创建了c后创建的a,因为这三条语句并不影响执行结果;
这时加上volatile:

int a = 1;
int b = 0;
volatile int c = 5;

那么,c一定再a之后才会创建;

synchronized

定义了synchronized的类、实例对象、代码块、方法都会在编译后,形成两个引用类型 : monitorenter 和 monitorexit;用这两个对象明确的指向锁定和解锁的对象;
当一个线程尝试获取一个synchronized对象的锁时,monitorenter当前没有被其他线程锁定,monitorenter会对计0数+1;并把monitorenter指向线程本身的monitor;
当退出时,monitorexit会把计数-1,锁就被释放了;
由于获取锁时monitorenter时指向线程本身的monitor的,所有对于已经获取了锁的线程,时不需要再次尝试获取锁的;

重入锁(ReentrantLock)

ReentrantLock 是 java.util.concurrent 包下的一个重入锁实现类;
与synchronized不同,synchronized 是java原声语法层面的互斥锁;而 ReentrantLock 是api层面的互斥锁;
通过具体的代码,可以实现:

  1. 等待可中断;
    对于长时间获取不到锁的线程,可以中断等待,去执行其他指令;
  2. 公平;
    可以通过申请锁的时间顺序来依次公平的获取锁,通过一个链表数据结构定义了一个Qeueu来实现;synchronized是非公平的;
  3. 锁可以绑定多个条件;
    一个lock对象可以同时绑定多个condition来实现不同条件下的等待和唤醒操作,调用多个new Condition()即可;而synchronized只能隐含的实现一个条件的等待和唤醒;

锁的种类

  1. 从实现层面,分原声语法锁和api锁
  2. 从锁方式,分为互斥锁和读写锁
    互斥锁就是锁住的对象,任何操作都是要绝对互斥的;
    读写锁就是,读读共享锁,读写互斥。写写互斥;通过定义读锁和写锁,读锁计数和写锁计数来实现;
  3. 从锁效率,分为悲观所和乐观锁
    悲观所就是,任何时候都认为有竞争关系,获取锁对象就锁住对象;
    乐观锁就是,认为一般不会发生竞争关系,通过对象头设置标记位来设置锁,并设置版本号,当发生竞争时通过标记位来标记现在是存在竞争还是没有,用版本号来指定线程执行的操作是否有效,是否需要等待锁并重新执行;
  4. 从锁量级,分偏向锁,轻量级锁、重量级锁
    偏向锁是几乎不存在竞争的情况下,一个线程获取过该对象的锁,那么这个线程下次再尝试获取锁时,如果锁依然保持偏向状态,并且偏向的就是该线程,可以直接获取锁;偏向状态和偏向的线程都再对象头中记录;如果时非偏向状态,则升级为轻量级锁;
    轻量级锁,也是再对象头中设置一个标记位,代表锁是轻量级的,并再对象头中设置owner指向,尝试设置owner及锁定状态即可,不需要真的锁住对象;当发生竞争,即尝试获取锁时发现锁已经存在owner并是锁定状态,则升级锁为重量级锁;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值