Synchronized

Synchronized基本描述

·对于普通同步方法,锁是当前实例对象。
·对于静态同步方法,锁是当前类的Class对象。
·对于同步方法块,锁是Synchonized括号里配置的对象。

JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的是 ACC_SYNCHRONIZED 标识

monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处

任何对象都有一个monitor与之关联,当一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

// 锁方法
public synchronized void add(){
       i++;
}
 public synchronized void add();
    descriptor: ()V
    flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: dup
         2: getfield      #2                  // Field i:I
         5: iconst_1
         6: iadd
         7: putfield      #2                  // Field i:I
        10: return
            
// 锁代码块
public void add() {
    synchronized (this) {
        i++;
    }
}

public void add();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter    // synchronized关键字的入口
       4: getstatic     #2                  // Field i:I
       7: iconst_1
       8: iadd
       9: putstatic     #2                  // Field i:I
      12: aload_1
      13: monitorexit  // 正常程序执行synchronized关键字的出口
      14: goto          22
      17: astore_2
      18: aload_1
      19: monitorexit // 抛异常时synchronized关键字的出口
      20: aload_2
      21: athrow
      22: return

Java对象头

synchronized用的锁是存在Java对象头里的

不同类型对象,对象头的字宽说明,在32位虚拟机中,1字宽等于4字节,即32bit

  • 数组类型:3字宽
  • 非数组:2字宽

对象头组成

  • Mark Word(1字宽)
  • 对象数据类型指针(1字宽)
  • 数组长度(这就是数据多的一个字宽的用处)

Mark Word

无锁状态

运行期间

口诀:25+4+1+2,2️⃣ 5️⃣法则:2锁,5针

口诀:00轻、10重、11GC、01偏

特殊案例分析

1.synchronized(this),锁的对象是this,是类的实例。
2.synchronized(object),锁的对象是object。

class STest{
 
    public void print(){
        synchronized (this){
            System.out.println("xxxx");
        }
    }
}

public class SynchronizeMain {

    public static void main(String[] args) throws InterruptedException {

        STest sTest = new STest();

        // Thread 1
        Thread t1 = new Thread(() -> {
            sTest.print();
        });

        // Thread 2
       Thread t2 = new Thread(() -> {

           try {
               synchronized (sTest){
                   while (true);
               }
           } catch (Exception e) {
               System.out.println("Exception="+e.getMessage());
           }

        });

       // 抢占了 sTest 锁,属于同步块锁,锁的是 sTest 对象
       t2.start();
       // sTest.print()永远无法执行
       t1.start();
    }
}

如果第一种方式用锁,如果使用这种方式,一旦锁对象(实例)被别人获取,那么线程将会被挂起,这就是说,当实例方法被别的线程锁住,就有可能造成正常锁代码块无法执行。

如果第二种方式用锁,除非都调用方法块内的方法,否则互不影响。

posted @   八月的冰可乐  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示