什么是同步升级过程?针对这个问题,本文详细介绍了相应的分析和解决方法,希望能帮助更多想要解决这个问题的伙伴找到更简单易行的方法。
要理解Synchronized,首先要明确偏向锁、轻量锁和重量级锁。在使用方面,我们需要有等待/等待(时间)/通知/全部通知等。让我们介绍同步的过程和用法。
Synchronized的升级过程
(Java SE 1.6中为了减少获得锁和释放锁带来的 性能消耗而引入的偏向锁和轻量级锁)
Synchronized的升级顺序是 无锁--偏向锁--轻量级锁--重量级锁,顺内不可逆。
00-1010当一个线程访问同步代码块并获取锁时,会将锁偏置的线程ID存储在对象头和栈帧的锁记录中,偏置锁是可重入锁。未来在进入和退出同步代码块时,线程不需要CAS操作进行锁定和解锁,只需要简单的测试一下对象头的 Mark Word 里是否存储着指向当前线程的偏向锁(当前线程的线程ID).如果测试成功,则意味着线程已经获得了锁;如果测试失败,需要测试Mark Word中的偏置锁标志是否设置为1(表示当前是偏置锁);如果偏置锁标志为1,则使用 CAS 进行锁获取,和偏置锁标志不为1,则尝试使用CAS 将对象头的偏向锁指向当前线程和上述两个CAS来获取锁,如果CAS操作成功则获取到了偏向锁和失败则代表出现了锁竞争,需要锁撤销操作.
00-1010部分锁使用等待竞争出现才释放锁,的机制,因此当其他线程试图争夺部分锁时,持有部分锁的线程将释放锁。部分锁的撤销需要一个等待拥有偏向锁的线程到达全局安全点(此时没有字节码被执行),它将首先暂停拥有偏向锁的线程,然后检查持有部分锁的线程是否是活动的。如果线程未激活,锁定对象的对象头将被设置为解锁状态。如果线程还活着,执行* *(判断是否需要持有锁),遍历偏向对象的锁记录,查看使用情况,如果还需要持有偏向锁,则偏向锁.带有偏置锁的Stacks会升级为轻量锁,如果不再需要偏置锁,锁对象会恢复到解锁状态,最终唤醒挂起的线程。
在00-1010线程执行同步块之前,JVM会先在当前线程的栈帧中创建一个存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称之为移置Mark Word。然后线程尝试使用CAS用指向锁定记录的指针替换对象头中的标记字。如果成功,当前线程获得锁;如果失败,这意味着其他线程争夺锁,当前线程试图使用自旋来获取锁,自旋有一定次数,如果超过设置自旋的次数则升级到重量级锁和或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止CPU空转.
偏向锁
轻度解锁时,将使用原子CAS操作将移位的标记字替换回对象头。如果成功了,就意味着没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁.
* *例如:*T1线程持有锁,T2线程旋转,但是T2线程的最大旋转次数已经过了,所以旋转失败,锁升级为重量级锁。T2线程被阻塞。此时,T1已经完成了同步代码块的执行,并解锁了轻量级锁。但是此时Mark Word中的标志位已经从00(偏置锁)变为10(中级锁),所以CAS解锁时会失败,T1会。
锁撤销
Synchronized是一个不公平的锁。当一个线程进入阻塞队列时,等待的线程会先尝试获取锁,如果无法获取,就会进入阻塞队列,这对已经进入队列的线程显然是不公平的。
轻量级锁
锁定优缺点偏向锁定的场景。
>加解锁不需要过多的资源消耗,和非同步方法的相比仅仅是纳秒的差距
流程图
Synchronized 方法
1: Synchronized 是java的内置锁,也是排它锁和非公平锁,排它锁也就是当前线程获取锁后,其他线程都会阻塞挂起 ,非公平锁是在线程后去锁的时候会先尝试后去锁,获取不到在进行阻塞。
2: Synchronized 是如何保证 '原子性' 的?是因为进入 Synchronized 块的内存语义是把 Synchronized 块内使用的 '工作内存清除', 这样在使用共享变量时就会直接存主内存中获取并复制到工作你内存中,在退出 Synchronized 语句块时 会把 工作内存中计算过的 '共享变量' 更新到主内存中。
3: 获取到 Synchronized 锁 ,都是 '对象锁'而非'代码块锁' (锁的都是对象或类,而不是某个方法),因此 Synchronized 是具有可重入性,在获取到该对象锁后可以不用再次获取该对象其他方法的锁,直接进入。
4: 如果是 Synchronized 用在 static 上, 就代表是类锁(.class),无论创建多少个对象都不可行;
区别
wait和sleep区别在于wait会释放锁, 但是sleep不会释放锁 ,sleep会导致线程阻塞挂起。
wait/wait(timeout)/notify/notifyAll 方法仅可以在获取到锁后才可以使用。
解释
wait: 线程等待。
wait(time): 线程等待,如果时间超过了设置的time,则继续执行。
notify: 随机唤醒一个等待的线程。
notifyAll: 唤醒全部等待线程。
代码演示
/** * @Auther: concurrenncy * @Date: 2019-03-25 16:43 * @Company: 随行付支付有限公司 * @maill: lan_tao@suixingpay.com * @Description: wait 和 sleep 区别在于 wait会释放锁, 但是 sleep 不会 ,sleep会导致线程阻塞挂起 */ public class WaitAndNotifyTest { private static Object obj = new Object(); public static void main(String[] args) { // 创建线程 thread1 Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " begin wait..."); synchronized (obj) { obj.wait(); } System.out.println(Thread.currentThread().getName() + " end wait..."); } catch (Exception e) { e.printStackTrace(); } } }, "thread1"); // 创建线程 thread2 Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " begin wait..."); synchronized (obj) { obj.wait(); } System.out.println(Thread.currentThread().getName() + " end wait..."); } catch (Exception e) { e.printStackTrace(); } } }, "thread2"); // 启动 thread1.start(); thread2.start(); try { // 睡眠一秒 Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } // 如果调用 notify 的线程未获取 对象锁,在调用 notify 的时候会抛出 java.lang.IllegalMonitorStateException 异常 synchronized (obj) { // 唤醒 使用 obj 调用 wait 方法的其中一个线程 (随机) obj.notify(); // 唤醒 使用呢 obj 调用 wait 方法的所有线程 obj.notifyAll(); } } }
关于Synchronized升级过程是怎样的问题的解答就分享到这里了,希望
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/38063.html