Java内存模型

;Java内存模型(JMM):首先JMM是一个抽象的额概念,它并不不存在(Java内存区域是两个不同的概念);JMM描述的是一组规则和规范,这种规范描述的是程序运行中各个变量的访问方式;由于JVM的实体是线程,每个线程在JVM中运行时,JVM都会为其创建一个工作内存,该工作内存是属于线程私有的;Java内存模型规定,所有的变量都存放在主内存中,主内存是各个线程间共享的,但线程对变量的操作必须放在工作内存中;首先从主内存中拷贝变量的各自额工作内存中然后在工作内存中进行变量的相关操作,之后在将工作内存中的变量副本写入到主内存中,线程不能直接对主内存中的变量进行操作;

  1. 主内存:主要存放的是Java实例对象,所有线程创建的额实例对象都存放在这里,不管该实例对象是成员变量还是局部变量;也会包括共享的类信息,常量,静态变量等;由于这是线程之间的共享的区域,所以多个线程进行操作时可能会出现线程安全问题;
  2. 工作内存:主要存储当前方法的本地变量信息(工作内存中存放的是主内存中的变量副本)每个线程只能访问自己的工作内存,即线程中的额本地变量定义其它线程是不可见的;由于工作内存是每个线程间私有的,所以线程间是不会互相方法对方的工作内存,这也就使得不会产生线程安全问题;

;Java内存模型中的原子性,有序性,可见性;

  1. 原子性:即一个操作是不可能中断的;例如对于一个静态变量X,有两个线程同时执行它,A线程赋值为X=1;B线程赋值为X=2;不管线程如何运行,X的结果要么是1,要么是2;线程A和线程B之间的操作是不受影响的,这就是原子性操作,不可被中断的特点;(对于方法级或者代码块级别的原子性操作可以使用synchronize和重入锁ReentrantLock来保证)
  2. 有序性:对于单线程中,在本线程内所有的操作都是有序的,但在多线程下,在一个线程观察另一个线程,所有操作都是无序的,无序是因为发生了指令重排;(而工作内存与住主内存发生的同步延迟导致的可见性问题,可以使用synchronized关键字和volatile关键字解决;对于指令重排的可见性和有序性问题可以使用volatile关键字解决)
  3. 可见性:指一个线程对共享变量修改后,其他线程是可以立马可见的;对于串行程序来说,可见性是不存在的,当线程修改了这个变量的值,依次执行后面的操作,紧接着就可以读取到该变量修改后的值;但是对于多线程程序中,由于线程对共享变量的操作都是变量的值拷贝一份到各自的工作内存中进行相关的操作,然后在写入到主内存中,其他线程才可见,假如A线程对共享变量X操作赋值为1;在该变量还未写入到主内存中时,线程B又对该共享变量进行操作,由于线程间的工作内存不是共享的,所以AX的修改对线程B来说就不可见;这种工作内存与主内存同步延迟现象就造成了可见性问题;

,解决方案:

  1. 针对原子性: 除了JVM自身提供的对基本数据类型读写操作的原子性外,对于方法级别或者代码块级别的原子性操作,可以使用synchronized关键字或者重入锁(ReentrantLock)保证程序执行的原子性;
  2. 针对可见性: 而工作内存与主内存同步延迟现象导致的可见性问题,可以使用synchronized关键字或者volatile关键字解决,它们都可以使一个线程修改后的变量立即对其他线程可见。
  3. 针对有序性: 对于指令重排导致的可见性问题和有序性问题,则可以利用volatile关键字解决,

#Happens-before(先行发生原则):除了上述使用Synchronized关键字和volatile来解决线程执行中存在的原子性,有序性,可见性外,在保证效率的前提下,还可以使用先行发生原则”,使用该原则可以辅助保证程序执行的原子性,可见性,有序性; 它是判断数据是否存在竞争、线程是否安全的依据,happens-before 原则内容如下:

  1. 单一线程原则:在一个程序内,在程序前面的操作先行发生于后面的操作,也就是按照代码顺序执行;
  2. 锁规则:解锁(unlock)操作必然发生在后续同一个锁加锁(locl)之前,也就是说对于同一个锁解锁后,再加锁,那么加锁必须发生在解锁动作之后(同一个锁);(换言之:一个unlock操作先行发生于后面对同一个锁的lock操作)
  3. Volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作;这也就保证了volatile变量的可见性;
  4. 线程启动规则:Thread对象的start()方法调用先行发生于此线程的每一个动作, 即如果线程A在执行线程B的start方法之前修改了共享变量的值,那么当线程B执行start方法时,线程A对共享变量的修改对线程B可见;
  5. 传递性:如果A操作先行与B操作,B操作先行于C操作,那么A操作先行于C操作;
  6. 线程终止规则:线程所有的操作先行于线程的终结操作; (Join方法先行发生于Thread对象的结束)Thread.join()方法的作用是等待当前执行的线程终止。假设在线程B终止之前,修改了共享变量,线程A从线程B的join方法成功返回后,线程B对共享变量的修改将对线程A可见。
  7. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生;
  8. 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始;

以上8条原则,可以在不适用(synchronized|volatile)的情况下线程在执行时的原子性,有序性,可见性;

:Volatile关键字:

使用volatile修饰的变量具有以下两个特性:

  1. 修饰的变量具有可见性,当一个线程修改了这个变量的值,volatile保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新;
  2. 修饰的变量不允许线程内部缓存和重排序;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值