java-多线程2

争用条件

多个线程同时访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏(corrupted),这种现象称为争用条件。

线程1和线程2 分时&抢占

 

线程间通信

同步、互斥

同步:volatile。同步的实现:wait()、notify()、notifyAll()

互斥:synchronized

 

Java内存模型(JMM)

java虚拟机有自己的内存模型(Java Memory Model,JMM),JMM可以屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果。

JMM决定一个线程对共享变量的写入何时对另一个线程可见,JMM定义了关系:

1、共享变量存储在主内存(Main Memory)中,

2、每个线程都有一个私有的本地内存(Local Memory

3、本地内存保存了被该线程使用到的主内存的副本拷贝

4、线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。这三者之间的交互关系如下:

 

 

需要注意的是,JMM是个抽象的内存模型,所以所谓的本地内存,主内存都是抽象概念,并不一定就真实的对应cpu缓存和物理内存。当然如果是出于理解的目的,这样对应起来也无不可。

 

对于普通的共享变量来讲,比如status,线程A将其修改为true这个动作发生在线程A的本地内存中,此时还未同步到主内存中去;而线程B缓存了status的初始值false,此时可能没有观测到status的值被修改了,所以就导致了上述的问题。

这种共享变量在多线程模型中的不可见性如何解决呢?比较粗暴的方式自然就是加锁,但是此处使用synchronized或者Lock这些方式太重量级了,有点炮打蚊子的意思。比较合理的方式其实就是volatile。

 

volatile

1、具有内存可见性。所谓可见性,是指当一条线程修改了共享变量的值,新值对于其他线程来说是可以立即得知的。

2、轻量级的同步机制(synchronized通常称为重量级锁)。

3、这个写操作会导致其他线程中的缓存无效

4、volatile对于单个的共享变量的读/写具有原子性,但是像num++这种复合操作,volatile无法保证其原子性。

5、volatile还有一个特性:禁止指令重排序优化。

重排序是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。但是重排序也需要遵守一定规则:

1、重排序操作不会对存在数据依赖关系的操作进行重排序。

2、重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变

synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得有序性。

6、volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

 

final

修饰类、方法、变量。

修饰类时,表示该类不能被继承

修饰方法

《java编程思想》:使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。

修饰的方法不能被重写,但是能重载

类的private方法会隐式地被指定为final方法

 修饰变量,表示该变量是常量,只能被赋值一次,赋值后值不再改变修饰引用时,表示对其初始化后不能指向其他对象了,但该引用指向的对象的内容可变的。

修饰的变量必须要显示初始化。一种是在变量声明的时候初始化;另一种就是在这个变量所在的类的所有的构造函数中对这个变量赋初值。

修饰函数参数:当函数的参数类型声明为final时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。

posted @ 2018-10-24 15:31  huinina  阅读(69)  评论(0)    收藏  举报