Java多线程_1_Java内存模型_内存模型的3大特性

Java内存模型:

内存分布情况及其关系:

主内存:Java内存模型规定所有的变量都保存在主内存中
工作内存:每个线程都有自己的工作内存,保存了该线程使用到的变量的主内存副本拷贝
主内存与工作内存的关系:
线程对变量的所有操作都必须在自己的工作内存中进行,不能直接读写主内存中的变量
不同线程之间无法直接访问对方工作内存中的变量
线程间变量值的传递均需要通过主内存来完成

内存交互操作:

java内存模型定义了8个操作完成主内存与工作内存的交互操作

1.read: 把变量从主内存读取到工作内存中
2.load: 在read执行后, 把read得到的值存入工作内存中
3.use: 把工作内存中一个变量值传递给执行引擎
4.assign: 把一个执行引擎接收的数据复制给工作内存变量
5.store: 把工作内存中的一个变量的值传送到主内存
6.write: 在store执行后, 把store得到的值放入主内存的变量中
7.lock: 作用于主内存的变量
8.unclock

注:
read-load,store-write必须顺序执行,不需要连续执行(read a,read a,load b,load b),不可单独出现.

不允许一个线程丢弃它最近的assign的操作,即变量在工作内存中改变了之后必须把该变化同步到主内存中

不允许一个线程无原因地把数据从线程的工作内存同步回主内存中

一个新变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化的变量

一个变量在同一时刻只允许一条线程对其lock操作,但lock操作可以被同一条线程执行多次,多次执行lock后,只有执行相同次数的unclock,变量才能解锁

如果对一个变量执行lock操作,将会清空工作内存 中此变量的值,在执行引擎使用这个变量前,需要重新执行load/assign来初始化变量

如果一个变量没被lock锁定,则不允许对它执行unclock,unclock也一致

对变量执行unclock操作之前,必须把此变量同步到主内存中(执行store/write)

原子性:

互斥同步机制:

1.JVM实现的synchronized:
	同步一个代码块:
		public void f(){
			synchronized(this){.......}
		}
//只能作用于同一个对象, 不同的对象不同进行同步, 例如: 如果同步一个账户,两个人同时操作一个账户,账户加锁即可同步, 对于不同的账户不需要同步, 二者没有任何关系, 也就没有同步这一说法

	同步一个方法与同步代码块一致只能作用于同一个对象:
		public synchronized void f(){.....}
		
	同步一个类,作用于整个类,两个线程调用同一个类的不同对象也会产生同步:
		public void f(){
			synchronized (SynchronizedExample.class){
				.........
			}
		}
		
	同步一个静态方法,作用于整个类
		public synchronized static void f(){....}
			
2.JDK实现的ReentrantLock //java.util.concurrent(J.U.C)包中的锁
	public class LockTest{
		private Lock lock = new ReentrantLock();
		public void f(){
			lock.lock();
			try{
				.......
			}finally{
				lock.unlock();//释放锁,避免死锁
			}
		}
	}

上述二者的性能现在大致相同

ReentrantLock与synchronized的区别:
在ReentrantLock中当持有锁的线程长期不释放锁的时候, 正在等待的线程可以选择放弃, 改为处理其他事, synchronized不行
公平锁: 多个线程等待同一个锁, 必须按照申请锁的时间顺序获得锁, synchronized中非公平锁,ReentrantLock也是非公平的, 但也可以是公平的
ReentrantLock可以同时绑定多个Condition对象
注: 除非使用ReentrantLock的高级功能, 否则优先使用synchronized, ReentrantLock不是所有的JDK都支持, 使用synchronized不用担心没释放锁而导致死锁问题, JVM会释放锁

原子性总结

synchronized的修饰对象
修饰代码块:大括号括起来,作用于对象
修饰方法:方法名前使用, 作用于对象在方法内部使用synchronized代码块,与修饰方法时效果是一致的
修饰静态方法:整个静态方法,作用于所有对象
修饰类:类名前添加,作用于所有对象

注:

1.作用于对象时,不同调用对象之间不影响
2.使用synchronized的父类(synchronized不属于方法声明的一部分),子类继承后需要对其方法重新添加synchronized修饰,不然不能使用同步
特点:
	synchronized:不可中断锁,适用于竞争不激烈,可读性好的场景
	Lock:可中断锁,多样化同步,竞争激烈时能维持常态
	Atomic:竞争激烈时能维持常态,比Lock性能好

可见性:

定义: 指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
即: 当共享变量被修改后, 在其他线程修改之前, 会被立马同步到主内存中
导致共享变量在线程中不可见原因:

1.线程交叉执行,交叉过程中,当前线程还未来得及修改主内存值就已被其他线程修改了
2.重排序结合线程交叉执行,指令重排,不具有原子性,导致1.中出现的问题
3.共享变量更新不及时(不能及时从工作内存快速同步到主内存中)

Java多线程的可见性操作
在多线程环境下,一个线程对共享变量的操作对其他线程是不可见的。
Java提供了volatile来保证可见性,当一个变量被volatile修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,其他线程读取共享变量时,会直接从主内存中读取。
synchronize和Lock都可以保证可见性。synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中, 以此保证可见性。

synchronized与volatile可见性操作细节:

synchronized的可见性:
	1.线程解锁前(线程执行完同步模块之前),必须把共享变量的最新值刷新到主内存(从工作内存中把值放入主内存)
	2.线程加锁的时候,必须先清空工作内存中上次存储的共享变量的值,然后再从主内存重新读取最新的值(加锁与解锁是对同一把锁来说)
volatile的可见性:
	1.对volatile修饰的变量进行写操作的时候,会在写操作之后,加StoreStore屏障指令,将工作内存中变量值刷新到主内存中共享变量
	2.对volatile修饰的变量进行读操作的时候,会在读操作之前,加StoreLoad屏障指令,从主内存读取共享变量,载入工作内存(也就把以前工作内存存储的值覆盖)
final的可见性:
	final: 被final修饰的变量在构造函数中被初始化且没有发生this逃逸,其他线程就能看见final

注:
volatile的写操作:
StoreStore屏障禁止volatile指令上面的普通写和下面的volatile写操作重排序
StoreLoad屏障防止volatile写操作与之后volatile读写操作的指令重排
volatile的读操作:
在插入LoadLoad屏障的位置, 禁止该指令前以及该指令后的volatile的读写重排序操作
volatile不具有原子性, 不是线程安全的, 只适合于作为状态标记量, 对变量的写操作不依赖于当前值

有序性:

定义:即程序执行的顺序按照代码的先后顺序执行, 在本线程中观察, 所有的操作都是有序的
需要注意的是:
从其他线程看本线程中会发现所有的操作都是无序的, jvm对每个线程都会进行指令重排, 且重排都会不一样, 在多线程的情况下, 指令重排将会出错, 一般采用volatile/synchronized修饰, 不会出现指令重排, 确保每个时刻都只有一个线程在执行.

先行发生原则:

1.单一线程原则:单个线程内, 程序前面的操作先行发生与后面的操作
2.管程锁定原则:一个unclock操作先行发生于后面对同一个所的lock操作,必须释放锁后,才能对对象重新加锁
3.volatile变量原则:对一个变量的写操作先行发生于后面对这个变量的读的操作
4.线程启动:Thread对象的start操作先行发生于此线程的每一个操作
5.线程加入规则: Thread对象的结束先行发生于join()的返回
6.线程中断原则: 对线程interrupt()的调用先行发生于被中断线程代码检测到的中断时间的发生, 可以通过interrupt检测是否有中断发生
7.对象终结规则: 一个对象的初始化(从构造函数结束开始)先行发生于它的finalize方法的开始
8.传递性: A比B先发生, B比C先发生, 则A比C先发生

上面有错, 还请指出, 如果认为我写的还不错, 还请点个赞, 多多支持一下, O(∩_∩)O~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值