怎么联系做网站公司,惠州网站推广排名,拼多多推广引流软件免费,广州微信网站建设公司参考链接 文章目录一 基本使用1 三个作用2 三种用法二 同步原理1 监视器 Monitor2 synchronized 用于同步代码块3 synchronized 用于同步方法3 Mark Word4 对象头的 Mark Word 和线程的 Lock Record三 锁的优化1 自旋锁2 锁消除3 锁粗化4 偏向锁5 轻量级锁、重量级锁以及三种锁…参考链接
文章目录一 基本使用1 三个作用2 三种用法二 同步原理1 监视器 Monitor2 synchronized 用于同步代码块3 synchronized 用于同步方法3 Mark Word4 对象头的 Mark Word 和线程的 Lock Record三 锁的优化1 自旋锁2 锁消除3 锁粗化4 偏向锁5 轻量级锁、重量级锁以及三种锁的对比*一 基本使用
1 三个作用 原子性确保线程互斥的访问同步代码即同一时间只有一个线程会进入同步代码块 可见性保证共享变量的修改能够及时可见依赖于 JMM 对一个变量 unlock 操作之前必须要同步到主内存中对一个变量进行 lock 操作则将会清空工作内存线程私有中此变量的值重新从主内存中 load 或 assign 初始化变量值 有序性虽然进行了重排序但保证只有一个线程会进入同步代码块单线程下的指令重排是安全的
2 三种用法
锁定实例方法时监视器锁monitor便是对象实例this锁定静态方法时监视器锁monitor便是对象的 Class 实例即锁定了这个类的所有实例锁定对象实例时监视器锁monitor便是括号括起来的对象实例
二 同步原理
数据的同步依赖锁锁的同步如何实现
synchronized 在软件层面依赖 JVM 实现锁的同步JUC.Lock 在硬件层面依赖特殊的 CPU 指令
1 监视器 Monitor
每个对象都对应一个监视器锁monitor用于实现重量级锁synchronized 在 JVM 里的实现都是基于进入和退出 Monitor 对象来实现方法同步和代码块同步每个 Java 对象的对象头的 Mark Word 中都存放着对应 Monitor 对象的引用所以任意对象都可以作为锁Monitor 对象包含两个队列 _EntryList 和 _WaitSet 分别存放未获取到 Monitor 对象的线程以及曾经获取到并且再次等待 Monitor 对象的线程 当多个线程同时访问一段同步代码时首先会进入 _EntryList 集合当线程获取到对象的 monitor 后进入 _Owner 区域并把 monitor 中的 owner 变量设置为当前线程同时 monitor 中的计数器 count若线程调用 wait() 方法将释放当前持有的 monitorowner 变量恢复为 nullcount–同时该线程进入 _WaitSet 中等待被唤醒若当前线程执行完毕释放 monitor 并复位 count以便其他线程进入获取 monitor
2 synchronized 用于同步代码块
反编译后可以得到由 monitorenter、monitorexit 两种指令实现 monitorenter当monitor被占用时就会处于锁定状态线程执行 monitorenter 指令时尝试获取对象的 monitor 的所有权过程如下 如果 monitor 的进入数为0则该线程进入monitor然后将进入数设置为1该线程即为 monitor 的所有者如果线程已经占有该 monitor只是重新进入则进入 monitor 的进入数加1 可重入性如果其他线程已经占用了 monitor则该线程进入阻塞状态直到 monitor 的进入数为0重新尝试获取 monitor 的所有权 monitorexit执行 monitorexit 的线程必须是 monitor 的所有者。指令执行时monitor的进入数减1如果减1后进入数为0那线程退出 monitor不再是这个 monitor 的所有者。monitorexit 插入在方法结束处和异常处JVM保证每个 monitorenter 必须有对应的 monitorexit
3 synchronized 用于同步方法
方法的同步并没有通过指令 monitorenter 和 monitorexit 来完成两种同步方式本质上没有区别只是方法的同步是一种隐式的方式来实现无需通过字节码来完成相对于非同步方法同步方法的常量池中多了 ACC_SYNCHRONIZED 标示符当方法调用时调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置如果设置了执行线程将先获取monitor获取成功之后才能执行方法体方法执行完后再释放 monitor 。在方法执行期间其他任何线程都无法再获得同一个monitor 对象
3 Mark Word Class Pointer类型指针对象指向它的类元数据的指针虚拟机通过这个指针来确定这个对象是哪个类的实例Mark Word标记字段用于存储对象自身的运行时数据是实现轻量级锁和偏向锁的关键。- 每个 Java 对象的对象头的 Mark Word 中都存放着对应 Monitor 对象的引用所以任意对象都可以作为锁。为了在很小的内存中尽可能存储更多的数据它的结构会随着程序的运行发生变化
4 对象头的 Mark Word 和线程的 Lock Record
在线程进入同步代码块的时候如果此同步对象没有被锁定则虚拟机首先在当前线程的栈中创建称为“锁记录Lock Record”的空间这个空间是线程私有的用于存储锁对象的 Mark Word 的拷贝每一个被锁住的对象的 Mark Word 都会和获得这个锁的线程的 Lock Record 关联对象头的 Mark Word 中的 Lock Word 指向 Lock Record 的起始地址Lock Record 中有一个 Owner 字段存放拥有该锁的线程的唯一标识表示该锁被这个线程占用 三 锁的优化
1 自旋锁
自旋锁当一个线程尝试获取某个锁时如果该锁已被其他线程占用就一直循环检测锁是否被释放而不是进入线程挂起或睡眠状态使用自旋锁的理由CPU 在用户态和核心态的切换需要消耗资源大多情况下锁状态的持续时间很短自旋锁适用于锁保护的临界区很小的情况临界区很小的话锁占用的时间就很短。自旋等待不能替代阻塞虽然它可以避免线程切换带来的开销但是它占用了CPU处理器的时间所以要规定适当的自旋次数适应性自旋锁线程如果自旋成功了那么下次自旋的次数会更加多反之会减少自旋次数甚至取消
2 锁消除
在有些情况下JVM 检测到不可能存在共享数据竞争这时会对这些同步锁进行锁消除在运行这段代码时JVM 可以明显检测到变量 vector 没有逃逸出方法所以可以将 vector 内部的加锁操作消除
public void vectorTest(){VectorString vector new VectorString();for(int i 0 ; i 10 ; i){vector.add(i );}System.out.println(vector);
}3 锁粗化
如果一系列的连续加锁解锁操作可能会导致不必要的性能损耗锁粗化将多个连续的加锁、解锁操作连接在一起扩展成一个范围更大的锁上述例子 vector 每次 add 都需要加锁JVM检测到对同一个对象连续加锁、解锁操作会合并一个更大范围的加锁、解锁操作即加锁解锁操作会移到循环之外
4 偏向锁
锁主要存在四种状态依次是无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态锁可以从偏向锁升级到轻量级锁再升级的重量级锁。但是锁的升级是单向的也就是说只能从低到高升级不会出现锁的降级
偏向锁是在单线程执行代码块时使用的机制如果在多线程并发的环境下一定会转化为轻量级锁或者重量级锁引入偏向锁主要目的是为了在没有多线程竞争的情况下尽量减少不必要的轻量级锁执行路径轻量级锁是为了在线程交替执行同步块时提高性能而偏向锁则是在只有一个线程执行同步块时进一步提高性能
5 轻量级锁、重量级锁以及三种锁的对比*
如果是单线程使用偏向锁的代价最小仅仅在内存中比较对象头即可无需 CAS如果出现了其他线程竞争则偏向锁就会升级为轻量级锁如果其他线程通过一定次数的 CAS 尝试没有成功则进入重量级锁