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

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 近场动力学matlab程序_一阶惯性环节matlab

    近场动力学matlab程序_一阶惯性环节matlab本发明属于过程控制技术领域,尤其涉及一种镇定一阶惯性加纯滞后系统的线性自抗扰控制器设计方法,进一步涉及一种用于具有时滞的工业过程控制系统的自抗扰控制器设计方法。背景技术:时滞作为一种常见的物理现象,在工业过程和生产生活中随处可见,例如管道对油气的输送、线缆对信号的传递、锅炉的燃烧等过程。这一类过程具有的共性即被控量不能立即对控制量的作用做出反应,这样的特点决定了被控对象输入与输出之间不同步的开环特…

    2022年9月27日
    1
  • Excel 读取并解密

    Excel 读取并解密packagecom;/****/importjava.io.File;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.io.FileOutputStream;importjava.io.IOException;importjav…

    2022年6月28日
    26
  • 一文深入讲解redis和couchbase的区别

    一文深入讲解redis和couchbase的区别

    2022年2月19日
    38
  • vue slot插槽_vue插槽的使用场景

    vue slot插槽_vue插槽的使用场景为什么使用slotslot(插槽)在生活中很多地方都有插槽,电脑usb的插槽,插板当中的电源插槽插槽的目的是为了让我们原来的设备具备更多的扩展性比如电脑的USB我们可以插入U盘,手机,鼠标,键

    2022年8月7日
    6
  • 快速阶乘算法python_【最全】阶乘算法!(python和C语言)

    快速阶乘算法python_【最全】阶乘算法!(python和C语言)阶乘的计算叁岁学编程:用最简单的大白话理解编程,欢迎大家关注,留言,提问,希望和大家一起提升!文章目录阶乘的计算阶乘定义:解析方法一:for循环计算方法二:定义for循环的函数计算方法三:定义递归函数计算小知识:C语言代码方法一:for函数方法二:递归函数总结:阶乘定义:阶乘指从1乘以2乘以3乘以4一直乘到所要求的数。例如所要求的数是4,则阶乘式是1×2×3×4,得到的积是24。24就是4的阶乘。…

    2022年7月24日
    9
  • arm cortex-a57 cpu_处理器a73和a53

    arm cortex-a57 cpu_处理器a73和a53在如今这个电子产品泛滥的年代,仅仅靠品牌或是外观已经不足以辨别产品的优劣,其内置的处理器自然也就成为了分辨产品是否高端的标准之一。那么我们今天就不妨好好了解一下近几年来电子产品中较为主流的RAM处理器。  在这之前让我们先简单认识一下处理器的架构。所谓处理器架构是CPU厂商给属于同一系列的CPU产品定的一个规范,主要目的是为了区分不同类型CPU的重要标示。目前市面上的CPU指令集分类主要分有…

    2022年8月31日
    16

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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