Java--synchronized--使用/原理/中断/锁的升级 不念不忘少年蓝@ 2022-09-17 03:19 129阅读 0赞 原文网址:[Java--synchronized--使用/原理/中断/锁的升级\_IT利刃出鞘的博客-CSDN博客][Java--synchronized--_IT_-CSDN] # 简介 # **作用** 保证原子性、可见性。 只要保证多个线程使用同一个“对象监视锁”,就能保证同步。 **用法** <table> <tbody> <tr> <td> <p><strong><strong>用法</strong></strong></p> </td> <td> <p><strong><strong>对象监视器</strong></strong></p> </td> <td> <p><strong><strong>示例</strong></strong></p> </td> </tr> <tr> <td> <p><strong>修饰普通方法</strong></p> </td> <td> <p>当前实例对象</p> </td> <td> <p>public synchronized void increase() { </p> <p> i++;</p> <p>}</p> </td> </tr> <tr> <td> <p><strong>修饰静态方法</strong></p> </td> <td> <p>当前类的class对象</p> </td> <td> <p>public static synchronized void increase() { </p> <p> i++;</p> <p>}</p> </td> </tr> <tr> <td> <p><strong>修饰代码块</strong></p> </td> <td> <p>括号里边的对象。</p> <p>此对象可以是<strong>实例对象</strong>(例:this),也可以是<strong>class对象</strong>(例:XXX.class或者this.getClass())。</p> </td> <td> <p>public Object synMethod(Object a1) { </p> <p> synchronized(a1) { </p> <p> // 操作</p> <p> }</p> <p>}</p> </td> </tr> </tbody> </table> **实例1:synchronized修饰普通方法,使用同一对象访问** package org.example.a; class MyThread implements Runnable{ @Override public synchronized void run() { System.out.println(1); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(2); } } public class Demo{ public static void main(String[] args) { MyThread myThread = new MyThread(); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread1.start(); thread2.start(); } } **执行结果**(结果同步,因为synchronized修饰普通方法时锁对象是this对象,使用一个对象去访问,是同一把锁。) 1 2 1 2 **实例2:synchronized修饰普通方法,使用不同对象访问** package org.example.a; class MyThread implements Runnable{ @Override public synchronized void run() { System.out.println(1); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(2); } } public class Demo{ public static void main(String[] args) { MyThread myThread1 = new MyThread(); Thread thread1 = new Thread(myThread1); MyThread myThread2 = new MyThread(); Thread thread2 = new Thread(myThread2); thread1.start(); thread2.start(); } } **执行结果**(结果不同步,因为synchronized修饰普通方法时锁对象是this对象,使用两个对象去访问,不是同一把锁。) 1 1 2 2 **实例3:synchronized修饰Class对象,使用不同对象访问** package org.example.a; class MyThread implements Runnable{ @Override public void run() { synchronized (MyThread.class) { System.out.println(1); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(2); } } } public class Demo{ public static void main(String[] args) { MyThread myThread1 = new MyThread(); Thread thread1 = new Thread(myThread1); MyThread myThread2 = new MyThread(); Thread thread2 = new Thread(myThread2); thread1.start(); thread2.start(); } } **执行结果**(结果同步,因为synchronized修饰的是一个类对象,是惟一的。) 1 2 1 2 **实例4**: **synchronized修饰静态方法,使用不同对象访问** 本处不能这样写: class MyThread implements Runnable{ @Override public static synchronized void run() { System.out.println(1); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(2); } } 会报错。因为Runnable接口的run方法是非静态类型,此处若使用静态,会提示没有覆写run方法。 # 原理 # **源码探寻** Test.java public class Test { private static Object LOCK = new Object(); public static int main(String[] args) { synchronized (LOCK){ System.out.println("Hello World"); } return 1; } } 先用javac Test.class 编译出class文件 再用javap –c Test.class查看字节码文件 字节码文件: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70][] 也就是说,锁是通过monitorenter和monitorexit来实现的。 **进入监视器** JVM规范中描述: monitorenter:\` Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: • If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. • If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. • If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership. \` 翻译: 每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下: 1. 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。 2. 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1. 3. 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。 **退出监视器** JVM规范中描述: monitorexit: \` The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.\` 翻译: 执行monitorexit的线程必须是objectref所对应的monitor的所有者。 指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。 Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。 # 中断 # **其他网址** [为什么Synchronized不可中断? - 云+社区 - 腾讯云][Synchronized_ - _ -] [【Java并发】synchronized不可以被中断?\_runhua的专栏-CSDN博客\_synchronized 中断][Java_synchronized_runhua_-CSDN_synchronized] [Synchronized之三:Synchronized与线程中断、线程wait - duanxz - 博客园][Synchronized_Synchronized_wait - duanxz -] **简介** Synchronized不可中断的说法:**只有获取到锁之后才能中断,等待锁时不可中断。** **代码** package org.example.a; public class Demo { private static final Object o1 = new Object(); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { System.out.println("t1: enter"); synchronized (o1) { try { System.out.println("t1: start lock"); Thread.sleep(5000); System.out.println("t1: end lock"); } catch (InterruptedException e) { System.out.println("t1: interruptedException"); e.printStackTrace(); } } System.out.println("t1: exit"); }); Thread thread2 = new Thread(() -> { System.out.println("t2: enter"); synchronized (o1) { try { System.out.println("t2: start lock"); Thread.sleep(1000); System.out.println("t2: end lock"); } catch (InterruptedException e) { System.out.println("t2: interruptedException"); e.printStackTrace(); } } System.out.println("t2: exit"); }); thread1.start(); //让thread1先执行 Thread.sleep(200); thread2.start(); // 中断t2线程的执行 thread2.interrupt(); System.out.println("main: t2 interrupt..."); } } # 锁的升级 # **其他网址** [synchronized锁升级就是这么的简单\_gongsenlin341的博客-CSDN博客\_synchronized升级过程][synchronized_gongsenlin341_-CSDN_synchronized] [synchronized 锁升级过程\_java叶新东老师的博客-CSDN博客\_synchronized 锁升级过程][synchronized _java_-CSDN_synchronized] [synchronized锁升级过程以及64位jvm的Mark Word\_Hai-Yang-code的博客-CSDN博客][synchronized_64_jvm_Mark Word_Hai-Yang-code_-CSDN] **简介** 1. JVM 4秒默认的偏向锁启动延迟 2. 若不考虑偏向锁的启动延迟,当仅有一个线程拿锁的时候,锁对象的状态是**偏向锁**状态 3. 当多个线程拿锁的时候,若是交替拿锁不发生竞争或者发生竞争线程自旋的次数小于某阈值,偏向锁升级成**轻量级锁**。 4. 当自旋的次数超过某阈值,轻量级锁升级成**重量级锁。** [Java--synchronized--_IT_-CSDN]: https://knife.blog.csdn.net/article/details/120962201 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc_size_16_color_FFFFFF_t_70]: /images/20220828/014c4e449f314e9b87a17fc0b820d950.png [Synchronized_ - _ -]: https://cloud.tencent.com/developer/article/1622813 [Java_synchronized_runhua_-CSDN_synchronized]: https://blog.csdn.net/runhua/article/details/98595867 [Synchronized_Synchronized_wait - duanxz -]: https://www.cnblogs.com/duanxz/p/4494420.html [synchronized_gongsenlin341_-CSDN_synchronized]: https://blog.csdn.net/gongsenlin341/article/details/112244837 [synchronized _java_-CSDN_synchronized]: https://blog.csdn.net/qq_27184497/article/details/116445215 [synchronized_64_jvm_Mark Word_Hai-Yang-code_-CSDN]: https://blog.csdn.net/weixin_45007916/article/details/107535746
还没有评论,来说两句吧...