Java_173_Thread_高级主题_指令重排_HappenBefore_volatile解决指令重排_DCL单列模式_double-checking_volatile-synchronized

指令重排

package TCPUDPThread;
/**
 * HappenBefore
 * 你写的代码很可能根本没按你期望的顺序执行,因为编译器和CPU会尝试重排指令使得代码更快地运行
 * 
 * 两个层面:一个是虚拟机层面,一个是硬件层面
 * 
 * 执行代码的顺序可能与编写代码不一致,即虚拟机优化代码顺序,则为指令重排happen-before即:编译器货运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段.
 * 在虚拟机层面,为了尽可能减少内存操作速度运慢于CPU运行速度所带来的CPU空置的影响,虚拟机会按照自己的一些规则(这规则后面在叙述)将程序编写顺序打乱--即写在后面的代码在时间
 * 顺序上可能会先执行,而写在前面的代码会后执行--以尽可能充分的利用CPU.拿上面的例子来说:假如不是a=1的操作,而是a=new byte[1024*1024](分配1M空间),那么它会运行的很慢,对此
 * CPU是等待其结束呢,还是先执行下面的那语句flag=true呢?显然,先执行flag=true可以提前使用CPU,加快整体效率,当然这样的前提是不会产生错误(什么样的错误后面再说).虽然这里有两种抢矿:
 * 后面的代码先于前面的代码开始执行;前面的代码先开始执行,但当效率较慢的时候,后面的代码开始执行并先于前面的代码执行结束.不管谁先开始,总之后面的代码在一些情况下存在先结束的可能.
 * 
 * 在硬件层面,CPU会将接受到的一批指令按照其规则重新排序,同样是基于CPU速度比缓存速度块的原因,和上一点的目的类似,只是硬件处理的话,每次只能在接收到的有限指令范围内重排序,而虚拟机可以在更大层面,更多指令范围内重排序.
 * 
 * 数据依赖
 * 
 * 如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖.
 * 数据依赖分下列三种类型:
 * 写后读 a=1;b=a; 写一个变量之后,在读这个位置.
 * 写后写 a=1;a=2; 写一个变量之后,在写这个变量.
 * 读后写 a=b;b=1; 读一个变量之后,在写这个变量.
 * 上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变.所以,编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序.
 * 
 * 指令重排:代码执行顺序与预期不一致
 * 目的:提高性能
 * @author pmc
 *
 */
public class HappenBeforeTest {
	//变量1
	private static int a=0;
	//变量2
	private static boolean flag=false;
	public static void main(String[] args) throws Throwable{
		for(int i=0;i<10;i++){
			a=0;
			flag=false;
		//线程1 读取数据
		Thread t=new Thread(()->{
			a=1;
			flag=true;
		});
		//线程2 更改数据
		Thread t2=new Thread(()->{
			if(flag){
				a*=1;
			}
			if(a==0){
				System.out.println("happen before a->"+a);
			}
		});
	
		
		t.start();
		t2.start();
	
		t.join();
		t2.join();
		
		System.out.println(a);
		}
		}
}

volatile 简称轻量级synchronized

package TCPUDPThread;
/**
 * volatile保证线程间变量的可见性,简单的说就是当线程A对变量进行了修改后,在线程A后面执行的其他线程能看到变量X的变动,更详细的说是要复核以下两个规则:
 * 线程对变量进行修改之后,要立刻回写到主内存.
 * 线程对变量读取的时候,要从主内存中读,而不是缓存.
 * java线程-工作内存Save和load操作-主内存
 * java线程-工作内存Save和load操作-主内存
 * java线程-工作内存Save和load操作-主内存
 * 各线程的工作内存间彼此独立,互不可见,在线程启动的时候,虚拟机为每个内存分配一块工作内存,不仅包含了线程内部定义的局部变量,也包含了线程所需要使用的共享变量(非线程内构造的对象)的副本,即为了提高执行效率.
 *
 * volatile是不错的机制,但是volatile不能保证原子性.
 * @author pmc
 *
 */
public class volatileTest {
	private volatile static int num=0;//使用volatile
	public static void main(String[] args) throws Exception {
		new Thread(()->{
			while(num==0){
				
			}
		}).start();
		Thread.sleep(1000);
		System.out.println(num=1);
	}
}

DCL单列模式,double-checking,volatile、synchronized(name.class) 锁静态类对象

package TCPUDPThread;
/**
 * DCL单列模式:懒汉式套路基础上加入并发控制,保证在多线程环境下,对外存在一个对象
 * 1.构造器私有化-->避免外部new构造器
 * 2.提供私有的静态属性-->存储对象的地址
 * 3.提供公共的静态方法-->获取属性
 * 
 * double-checking
 * volatile
 * 
 * volatile可以避免指定重排问题
 * 
 * @author pmc
 *
 */
public class dclTest {
	//1.私有构造器
	private dclTest(){
		
	}
	//2.提供私有的静态属性
	private volatile static dclTest instance;
	
	public static dclTest getInstance() {
		//再次检测
		if(instance!=null){//避免不必要的同步,已经存在对象
			return  instance;
		}
		synchronized(dclTest.class){
		if(instance==null){
			instance=new dclTest();
			//1.开辟空间2.初始化对象信息3.返回对象的地址给引用
		}
		}
		return instance;
	}

	public static dclTest getInstance1(long time) {
	
		if(instance==null){
			try {
				Thread.sleep(time);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			instance=new dclTest();
			//1.开辟空间2.初始化对象信息3.返回对象的地址给引用
		}
		return instance;
	}

	public static void setInstance(dclTest instance) {
		dclTest.instance = instance;
	}



	public static void main(String[] args) {
//		Thread t=new Thread(()->{
//			System.out.println(dclTest.getInstance());
//		});
//		t.start();
//		System.out.println(dclTest.getInstance());
		
		Thread t1=new Thread(()->{
			System.out.println(dclTest.getInstance1(500));
		});
		t1.start();
		System.out.println(dclTest.getInstance1(1000));
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr_Pmc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值