怎么查询公司名字是否可以注册,沈阳优化网站,wordpress登录查看插件,公司网站设计与实现文章目录 原子性、有序性、可见性原子性有序性可见性 synchronized使用synchronized锁升级synchronized-ObjectMonitor 原子性、有序性、可见性
原子性
数据库事务的原子性#xff1a;是一个最小的执行的单位#xff0c;一次事务的多次操作要么都成功#xff0c;要么都失败… 文章目录 原子性、有序性、可见性原子性有序性可见性 synchronized使用synchronized锁升级synchronized-ObjectMonitor 原子性、有序性、可见性
原子性
数据库事务的原子性是一个最小的执行的单位一次事务的多次操作要么都成功要么都失败。
并发编程的原子性一个或多个指令在CPU执行过程中不允许中断。
i;操作是原子性
肯定不是i操作一共有三个指令 getfield从主内存拉取数据到CPU寄存器
iadd在寄存器内部对数据进行1
putfield将CPU寄存器中的结果更新搭配主内存中
如何保证i是原子性
使用synchronized、lock、AtomicCAS来保证 使用lock锁也会有类似的概念也就是在操作i的三个指令前先基于AQS成功修改state后才可以操作
使用synchronized和lock锁时可能会触发将线程挂起的操作而这种操作会触发内核态和用户态的切换从而导致消耗资源。
CAS方式就相对synchronized和lock锁的效率更高因为CAS不会触发线程挂起操作
CAScompare and swap
线程基于CAS修改数据的方式先获取主内存数据在修改之前先比较数据是否一致如果一致修改主内存数据如果不一致放弃这次修改
CAS就是比较和交换而比较和交换是一个原子操作 CAS在Java层面就是Unsafe类中提供的一个native方法这个方法只提供了CAS成功返回true失败返回false如果需要重试策略需要自己实现
CAS问题
CAS只能对一个变量的修改实现原子性。CAS存在ABA问题。 A线程修改主内存数据从1~2卡在了获取1之后。B线程修改主内存数据从1~2完成。C线程修改主内存数据从2~1完成。A线程执行CAS操作发现主内存是1没问题直接修改解决方案加版本号 在CAS执行次数过多但是依旧无法实现对数据的修改CPU会一直调度这个线程造成对CPU的性能损耗 synchronized的实现方式CAS自旋一定次数后如果还不成挂起线程LongAdder的实现方式当CAS失败后将操作的值存储起来后续一起添加
有序性
指令在CPU调度执行时CPU会为了提升执行效率在不影响结果的前提下对CPU指令进行重新排序。但是这样可能会造成数据的不一致。
如果不希望CPU对指定进行重排序怎么办
可以对属性追加volatile修饰就不会对当前属性的操作进行指令重排序。
可见性
CPU在处理时需要将主内存数据拿到寄存机中再执行指令执行完指令后需要将寄存器数据扔回到主内存中。但是寄存器数据同步到主内存是遵循MESI协议的简单来说就是不是每次操作结束就将CPU缓存数据同步到主内存这样就会造成多个线程看到的数据不一样。
所以通常需要synchronized和volatile配合解决这一问题
volatile每次操作后立即同步数据到主内存。synchronized只有一个线程操作这个数据。
synchronized使用
使用方法声明方法时使用synchronized或者在代码块中使用synchronized。 锁类型
类锁基于当前类的Class加锁对象锁基于this对象加锁
synchronized是互斥锁每个线程获取synchronized时基于synchronized绑定的对象去获取锁
synchronized是如何基于对象实现的互斥锁先了解对象再内存中是如何存储的。 在Java中查看对象的存储
导入依赖
dependencygroupIdorg.openjdk.jol/groupIdartifactIdjol-core/artifactIdversion0.9/version
/dependency查看对象信息 synchronized锁升级
synchronized在jdk1.6之前一直是重量级锁只要线程获取锁资源失败直接挂起线程。
jdk1.6之前synchronized效率贼低再加上Doug Lea推出了ReentrantLock效率比synchronized快多了导致JDK团队不得不在jdk1.6将synchronized做优化。
锁升级
无锁状态、匿名偏向状态没有线程拿锁。偏向锁状态没有线程的竞争只有一个线程在获取锁资源。 线程竞争锁资源时发现当前synchronized没有线程占用锁资源并且锁是偏向锁使用CAS的方式设置线程ID为当前线程获取到锁资源下次当前线程再次获取时只需要判断是偏向锁并且线程ID是当前线程ID即可直接获得到锁资源。轻量级锁偏向锁出现竞争时会升级到轻量级锁。 轻量级锁的状态下线程会基于CAS的方式尝试获取锁资源CAS的次数是基于自适应自旋锁实现的JVM会自动的基于上一次获取锁是否成功来决定这次获取锁资源要CAS多少次。重量级锁轻量级锁CAS一段次数后没有拿到锁资源升级为重量级锁其实CAS操作是在重量级锁时执行的。重量级锁就是线程拿不到锁就挂起。
偏向锁是延迟开启的并且在开启偏向锁之后默认不存在无锁状态只存在匿名偏向synchronized因为不存在从重量级锁降级到偏向或者是轻量。
synchronized在偏向锁升级到轻量锁时会涉及到偏向锁撤销需要等到一个安全点stw才可以撤销并发偏向锁撤销比较消耗资源。在程序启动时偏向锁有一个延迟开启的操作因为项目启动时ClassLoader会加载.class文件这里会涉及到synchronized操作。为了避免启动时涉及到偏向锁撤销导致启动效率变慢所以程序启动时默认不是开启偏向锁的。
编译器优化的结果出现了下列效果 锁消除线程在执行一段synchronized代码块时发现没有共享数据的操作自动帮你把synchronized去掉。 锁粗化在一个多次循环的操作中频繁的获取和释放锁资源synchronized在编译时可能会优化到循环外部。
synchronized-ObjectMonitor
ObjectMonitor一般是到达了重量级锁才会涉及到。在到达重量级锁之后重量级锁的指针会指向ObjectMonitor对象。 ObjectMonitor() {_header NULL;_count 0; // 抢占锁资源的线程个数_waiters 0, // 调用wait的线程个数。_recursions 0; // 可重入锁标记_object NULL; _owner NULL; // 持有锁的线程_WaitSet NULL; // wait的线程 双向链表_WaitSetLock 0 ;_Responsible NULL ;_succ NULL ; // 假定的继承人锁释放后被唤醒的线程有可能拿到锁资源_cxq NULL ; // 挂起线程存放的位置。单向链表FreeNext NULL ;_EntryList NULL ; // _cxq会在一定的机制下将_cxq里的等待线程扔到当前_EntryList里。 双向链表_SpinFreq 0 ;_SpinClock 0 ;OwnerIsThread 0 ;_previous_owner_tid 0;}