首页 > 编程知识 正文

lock_guard解决哪些问题,方法上的synchronized

时间:2023-05-04 17:02:54 阅读:15263 作者:432

文章: https://blog.csdn.net/Hu yiju/article/details/97126274http://www.Sina.com /

一、voliate相关

1:java内存模型

在计算机内存型号中,由于cpu和内存之间的速度有订单,所以引入缓存,告诉缓存哪个处理器的缓存为主,并与主内存同步。 此时,有缓存一致性协议,保证了缓存的一致性。

指令排序:例如在接下来的五行代码中,前四行计算机cpu中的执行顺序不必为12345,可以为13245或34125,但步骤5的顺序不变,该指令的排序不影响最后的计算结果。

int a=1; A; int b=5; B; int c=a b;1.1:计算机的内存模型

java内存模型可以阻止计算机硬件和操作系统之间的差异,但不会限制处理器使用缓存和主内存进行交互,也不会限制编译器在运行时对指令进行排序的优化。

1.2:java内存模型

voliate关键字有两个角色。 “可见性”和“禁止对命令重新排序”

1 )可见性:对于多个线程,确保当线程将一个int a的值更改为5时,其他线程也能立即知道int a=5。 实现方式是,在线程1中int a=5且在缓存中立即将a=5与主存储器同步,然后在其他线程使用a之前从主存储器刷新一次,将线程1改变后的值变为5。

2 )有序性)在意味着有序性的本线程内观察时,所有操作都有序进行(线程内为串行),但在另一个线程内观察本线程时,所有操作都是无序的),主要是指令排序现象和工作内存两个关键字(voliate和synchronize )确保线程之间的操作有序性。

2:Voliate关键字:voliate无法保证线程安全(可见性分析)。

预期结果: 10个线程同时运行,每个线程将I累计到1000。 最后的结果应该是10000。

实际结果:多次开车,大部分结果不到10000

结论I是线程1将I的值代入10,而不是原子操作。 这时,后线程2从可见性中也获得了10,但是线程1继续累积地将I值赋给250。 此时,线程2仍然接收到的值为10且过期数据,进入堆栈的值为11,将11与主存储器同步,并且线程1从主存储器同步的值为11。 此时,线程1的

publicclassvolitetest {//volia te关键字确保此变量在线程之间始终为公共静态卷输入id=1。 publicstaticvoidmain (字符串(args ) throwsinterruptedexception (/todo auto-generatedmethodstubthread ) ths=new thread food i10; I ) )/10线程并发ths(I )=newthread(newrunnable ) ) { @ override public void run }/todo auto-generatedmethodstubadd } ); ths[i].start (; //已完成聚合线程运行的while(thread.activecount )1) {Thread.yield; } system.out.println ('=========================(() () ) ) ) ) ) //该方法通过锁定实现线程安全公共静态语音添加() for ) intI=0; i1000; I ()/id不是原子的(/第一步)线程1如果同步id=5,此时其他线程进入(//第二部分)切换其他例如线程2同步id=5,执行很多操作,id=100

voliate保证cpu在该关键字前后的代码不进行指令的排序,保证按照书写顺序执行代码。

2.1

一个代码示例:确保线程安全,且只有一个实例

公共类danli { privatevolatilestaticdanlidanli; //专用结构,避免外部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

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。