voliate Synchronized Lock

voliate Synchronized Lock参考文章:https://blog.csdn.net/huyiju/article/details/97126274一、voliate相关1:java内存模型1.1:计算机的内存模型在计算机的内存模型中cpu和内存之间的速度存在数量级所以引入了高速缓存,告诉缓存会导致到底以哪个处理器的缓存为主,同步到主内存,这个时候有有了缓存一致性协议,来保证缓存一致性。指令重排:例如一下五行代码,前四行的在计算机cpu的执行顺序不一定是12345,也可以是13245或者34125,但是第五步的顺序不会变,这种

大家好,又见面了,我是你们的朋友全栈君。

参考文章:https://blog.csdn.net/huyiju/article/details/97126274
一、voliate相关

1:java内存模型

1.1:计算机的内存模型
在计算机的内存模型中cpu和内存之间的速度存在数量级所以引入了高速缓存,告诉缓存会导致到底以哪个处理器的缓存为主,同步到主内存,这个时候有有了缓存一致性协议,来保证缓存一致性。

在这里插入图片描述
指令重排:例如一下五行代码,前四行的在计算机cpu的执行顺序不一定是12345,也可以是13245或者34125,但是第五步的顺序不会变,这种指令重排不会影响最后的计算结果。

          int a=1;

          a++;

          int b=5;

          b++;

         int c=a+b;

1.2:java内存模型

java的内存模型屏蔽掉了计算机硬件和操作系统的差异,但是没有限制处理器使用缓存和主内存进行交互,也没有限制编译器在执行过程中的指令重排这类优化措施。
在这里插入图片描述

2:Voliate关键字
voliate关键字有两个作用(可见性和禁止指令重排)

1:可见性:保证在多个线程的情况下,线程一把int a的值修改为5的时候,其他线程也能立即知道int a=5,实现的方法是线程1把int a=5,会把a=5的是立马通过缓存同步到主内存,然后其他线程使用a之前会从主内存刷新一次,得到被线程1修改的值为5.

2:有序性:有序性的意思的在本线程内观察,所有操作都是有序的(线程内是串行的),但是在另一个线程观察本线程,所有操作都是无序的(主要是指指令重排现象和工作内存与主内存同步)。voliate和synchronize两个关键字来保证线程操作之间的有序性。

2.1:voliate不能保证线程安全(可见性分析)
预期结果:10个线程同时执行,每一个此线程都把i累加到1000.最后的结果应该是10000.

实际结果:多次运行,大部分结果都不足10000

结论分析:i++,不是原子性操作,线程1把i的值赋值的10,这个时候后线程2根据可见性也得到了10,但是线程1接着执行累加把i的值赋值到250,这个时候线程2依然拿到的值是10是过期数据,入栈++后的值为11,把11同步到了主内存,触发了线程1从主内存同步的得到值为11,这个时候会导致线程一的结果错误。

public class VoliteTest { 
   
	//voliate关键字保证了此变量在各个线程中是一致的
	public static volatile int id=1;
 
	public static void main(String[] args) throws InterruptedException { 
   
		// TODO Auto-generated method stub
		
		Thread[] ths=new Thread[10];
		for(int i=0;i<10;i++) { 
   
			//10个线程同时执行
			ths[i]=new Thread(new Runnable() { 
   
				@Override
				public void run() { 
   
					// TODO Auto-generated method stub
					add();
				}
			});
			ths[i].start();
			
		}
		
		//累加线程执行完毕
		while (Thread.activeCount()>1) { 
   
			Thread.yield();
		}
		System.out.println("========================");
		System.out.println("ID的值是:"+id);
	}
	
	//该方法可以通过加锁实现线程安全
	public  static void add() { 
   
		for(int i = 0;i<1000;i++) { 
   
			//id++不是原子性的
			//第一步:假如线程1同步id=5,这个时候要++,其他线程切换了进来
			//第二部:其他比如线程2同步id=5,执行了很多操作,id=100了
			//第三部:线程1执行++,得到了id=6,这个时候的id为5已经是过期数据了,
			//第四部:线程1将结果6同步到内存共享,其他的线程得到id=6.会导致最后的值不等于10001
		    id++;
		}
	}
 
}

2.2:voliate禁止指令重排
voliate保证cpu能够保证在这个关键字之前和之后的代码不会指令重排,保证按照书写顺序执行代码。

之所以能实现指令重排是因为voliate的可见性决定的,这个关键字保证了从缓存到主内存之间的同步,就像内存之间的一道屏障,保证了之前和之后的代码无法越过这个 内存栅栏。

单例代码案例:保证线程安全和只有一个实例

public class Danli { 
   
	
	private volatile static Danli danli;
	
	//私有构造,防止通过外部new创建对象
	private Danli() { 
   
		
	}
	
	//懒汉模式 需要的时候get
	//(方法加锁导致多线程效率低)
	public static synchronized Danli getDanli() { 
   
		if(danli==null) { 
   
			//在此处判断加锁,防止线程1判断为空后,线程二创建了实例
			//在加锁方法之内再次判断一次
			synchronized (Danli.class) { 
   
				if(danli==null) { 
   //再次判断
					danli=new Danli();//内存屏障,保证写完之后其他线程读取
					//1:在工作内存创建,(store存储)到主内存
					//在此之间可能有其他线程插入
					//2:主内存的danli(write写入)
                           //这是一个内存栅栏
				}
			}
		}
		return danli;
	}
 
}

二、synchronize关键字

2.1:说一下对synchronize关键字的理解
synchronize关键字解决了多线程的资源同步性,该关键字保证了在多线程的条件下,同时是有一个线程能够获取到资源。

在jdk的早起版本中,这个关键字是重量级锁,这个关键字在编译之后,在同步块的前后会形成monitorenter和monitorexit(监视器进入和退出,通过获取对象锁计数器加一减一来实现锁机制)两个节码指令,其他线程在竞争的时候会挂起,效率低下,但是随着jdk的发展,通过对synchronize底层的发展,不必每次线程都挂起。来大大提升了效率

比如采用偏向锁、轻量级锁、锁粗化、锁自旋、锁自旋、锁消除等手段来提升效率

2.2:synchronize在用项目中用到了吗?
2.2.1:synchronize用法
1:用来修饰静态方法(给类加了锁,无论new多少个对象,都会产生竞争,线程1new的对象获取普通加锁方法和线程2获取类的静态加锁方法不会产生竞争)

2:用来修饰静态代码块(也是给类加了锁)

3:用来修饰普通方法(锁给了对象,只用new的同一对象才有竞争)

2.2.2:项目中的使用(懒汉单例模式)

package com.thit.connpool;
 
public class LazySimple { 
   
	//voliate修饰,禁止指令重排
	private static volatile LazySimple lazySimple;
	//私有构造防止new创建对象
	private LazySimple() { 
   
		
	}
	//静态方法外部得到实例
	public static LazySimple getDanli() { 
   
		//首先判断是否为空
		if (lazySimple==null) { 
   
			synchronized (LazySimple.class) { 
   
				//再次判断是否为空
				if(lazySimple==null) { 
   
					//new 对象非原子性操作
					//1:分配内存空间
					//2:初始化值(构造器)
					//3:将对象指向内存地址
					lazySimple=new LazySimple();
				}
			}
		}
		return lazySimple;
	}
}

代码分析:

1:其中 volatile修饰lazySimple;是为了防止指令重排,其中lazySimple=new LazySimple();这段代码不是原子性操作,实际分为3步

                //1:分配内存空间
                //2:初始化值
                //3:将引用指向内存地址
                lazySimple=new LazySimple();

由于jvm有指令重排的优化,所以代码执行顺序可能是1>3>2,当线程1执行1>3的时候没有执行到2初始化赋值的时候,线程2进入执行,这个时候判断到lazySimplenull,会在创造一个对象,就不是单例了。加了voliate关键字修饰,会因为可见性而防止指令重排,因为其他的线程想要判断lazySimplenull的时候,会在1>2>3执行完成,在线程1修改lazySimple的时候,没写入之前内存像是有屏障一般。这边形成了指令重排无法越过的内存屏障。

三、说说synchronize关键字和voliate关键字
1:voliate修饰变量多线程可见性但是没有原子性,synchronize修饰方法能保证可见性和原子性。

2:voliate的效率更高一点,线程不会阻塞,但是synchronize线程会阻塞

3:voliate用来解决变量的多线程可见性,但是synchronize用来解决多线程资源访问问题

四、lock接口
4.1:为什么需要Lock接口?

我们知道锁是来控制多线程并发访问资源的竞争,在Lock接口出现之前我们使用synchronize关键字来实现锁功能,但是synchronize获取锁是隐式的,我们无法控制锁的获取,释放中断的可操作性性,由于synchronize获取锁固话,所以创建了Lock接口来实现更领过的锁。

Lock和synchronize都是重入锁

1:尝试非中断的获取锁,

2:可以实现中断,与synchronize关键字不同,当获取锁的线程能够响应中断,中断的时候,异常内抛出,释放锁

3:能指定时间获取锁,在指定时间内无法获取锁的时候返回

4.2:代码实现

Lock lock=new ReentrantLock();
		lock.lock();//获取锁
		try { 
   
			
		} catch (Exception e) { 
   
			e.printStackTrace();
		}finally { 
   
			lock.unlock();//finally修饰,异常处理,一定要释放锁
		}

原文链接:https://blog.csdn.net/huyiju/article/details/97646152

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/139668.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • win7、win10进程pid4占用80端口的解决办法

    win7、win10进程pid4占用80端口的解决办法

    2021年10月12日
  • 无法获得下列许可 solidworks_2019年个人总结

    无法获得下列许可 solidworks_2019年个人总结通常,按照网上教程来安装SolidWorks2019、2020到最后启动软件有可能会出现这个错误;下面提供本人亲测可用、在用的解决办法供各位参考。1.如果弹出该错误提示时,先检查“SolidWorks_Flexnet_Server”这个文件夹的所在路径,建议移动到SolidWorks的安装根目录下,即“SOLIDWORKSCorp”目录下。因自身原因无法移动的,请勿删除该文件夹,否则会激活…

  • qmk固件是什么_qt5svg.dll

    qmk固件是什么_qt5svg.dll操作步骤:1.安装qtp10.02.拷贝mgn-mqt82.exe到C:\ProgramFiles\MercuryInteractive(创建)文件夹下3.创建C:\ProgramFiles\CommonFiles\MercuryInteractive\LicenseManager文件夹4.执行mgn-mqt82.exe5.复制lservrc,整个电脑搜索,搜索出来之后使用t…

  • 单片机c语言程序设计试卷_stm32常见面试题

    单片机c语言程序设计试卷_stm32常见面试题c语言面试题集(单片机).c语言面试题集(单片机)1.预处理器(Preprocessor)1.用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)#defineSECONDS_PER_YEAR(60*60*24*365)UL:1).#define语法的基本知识(例如:不能以分号结束,括号的使用,等等)2).懂得预处理器将为你计算常数表达式的值,因…

  • QQ微信都是腾讯的吗_腾讯为什么放弃qq

    QQ微信都是腾讯的吗_腾讯为什么放弃qq中国第一大APP是哪一个?当然是微信。那么第二大APP是哪一个呢?答案是已经21岁的互联网“化石级”产品——QQ!QQ的今天月活用户居然高达7.35亿,稍微观察一下就会发现一个奇怪的现象…

  • DSP效果器调音教程_DSP控制器

    DSP效果器调音教程_DSP控制器1)DSP输出给5V的电路(如D/A),无需加任何缓冲电路,可以直接连接。3)的JTAG口的信号也必须为3.3V,否则有可能损坏DSP。dsp教程24。为什么要片内RAM大的DSP效率高?目前DSP发展的片内存储器RAM越来越大,要设计高效的DSP系统,就应该选择片内RAM较大的DSP。片内RAM同片外存储器相比,有以下优点:1)片内RAM的速度较快,可以保证DSP无等待运行。2)对于C2000…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号