Synchronized相关问题

Synchronized相关问题

Synchronized 锁信息是存储在哪里的?

  • 当 synchronized 修饰普通方法时,锁信息是存储在 this 对象中头的

  • 当 synchronized 修饰静态方法时,锁信息是存储在 当前类Class对象头中的

  • 当 synchronized(obj) 修饰同步代码块时,锁信息是存储在obj对象头中的(不能修饰基本数据类型,不可修饰null对象,可以修饰 String obj = "123"、String obj ="")

怎么证明锁信息就是在那里的?

一、当 synchronized 修饰普通方法时,锁信息是存储在 this对象中头的

实验思路

  1. 在使用锁之前,锁信息肯定没有存储,这时打印当前对象信息

  2. 在使用锁的时,锁的信息肯定已经存储,这时打印当前对象信息

  3. 在使用锁之后,锁的信息肯定已经消除,这时打印当前对象信息

预想结果

如果第一步和第三步的对象头信息相同,第二步与第一、第三步的对象头信息不同

则证明对象头信息是存储在当前对象的对象头中

实验代码

public class T001_Synchronized_Method {

    synchronized void m()  {
        LockSupport.park();
    }


    public static void main(String[] args) throws InterruptedException {
        T001_Synchronized_Method test = new T001_Synchronized_Method();

        System.out.println("========== 加锁之前 ==========");
        System.out.println(ClassLayout.parseInstance(test).toPrintable());

        System.out.println("========== 加锁中 ==========");
        Thread thread = new Thread(() -> test.m());
        thread.start();
        System.out.println(ClassLayout.parseInstance(test).toPrintable());

        LockSupport.unpark(thread);
        System.out.println("========== 释放锁之后 ==========");
        System.out.println(ClassLayout.parseInstance(test).toPrintable());
    }
}

实验结果

========== 加锁之前 ==========
com.yang.base.thread.A002_Synchronized.T001_Synchronized_This object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

========== 加锁中 ==========
com.yang.base.thread.A002_Synchronized.T001_Synchronized_This object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           a0 49 26 0b (10100000 01001001 00100110 00001011) (187058592)
      4     4        (object header)                           03 00 00 00 (00000011 00000000 00000000 00000000) (3)
      8     4        (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

========== 释放锁之后 ==========
com.yang.base.thread.A002_Synchronized.T001_Synchronized_This object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

实验结论

我们可以观察到对象头信息确实与我们预想的一样,证明:当 synchronized 修饰普通方法时,锁信息是存储在 this 对象中头的

二、当 synchronized 修饰静态方法时,锁信息是存储在 当前类Class对象头中的

实验思路

  1. 在使用锁之前,锁信息肯定没有存储,这时打印当前类的Class 信息

  2. 在使用锁的时,锁的信息肯定已经存储,这时打印当前类的Class 信息

  3. 在使用锁之后,锁的信息肯定已经消除,这时打印当前类的Class 信息

预想结果

如果第一步和第三步的对象头信息相同,第二步与第一、第三步的对象头信息不同

则证明对象头信息是存储在当前类的Class的对象头中

实验代码

public class T001_Synchronized_Static_Method {

    synchronized static void m()  {
        LockSupport.park();
    }


    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== 加锁之前 ==========");
        System.out.println(ClassLayout.parseInstance(T001_Synchronized_Static_Method.class).toPrintable());

        System.out.println("========== 加锁中 ==========");
        Thread thread = new Thread(() -> m());
        thread.start();
        System.out.println(ClassLayout.parseInstance(T001_Synchronized_Static_Method.class).toPrintable());

        LockSupport.unpark(thread);
        System.out.println("========== 释放锁之后 ==========");
        System.out.println(ClassLayout.parseInstance(T001_Synchronized_Static_Method.class).toPrintable());
    }
}

实验结果

========== 加锁之前 ==========
java.lang.Class object internals:
 OFFSET  SIZE                                              TYPE DESCRIPTION                               VALUE
      0     4                                                   (object header)                           01 a4 ba 5f (00000001 10100100 10111010 01011111) (1606067201)
      4     4                                                   (object header)                           13 00 00 00 (00010011 00000000 00000000 00000000) (19)
      8     4                                                   (object header)                           df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
......

========== 加锁中 ==========
java.lang.Class object internals:
 OFFSET  SIZE                                              TYPE DESCRIPTION                               VALUE
      0     4                                                   (object header)                           b0 89 0a 0e (10110000 10001001 00001010 00001110) (235571632)
      4     4                                                   (object header)                           03 00 00 00 (00000011 00000000 00000000 00000000) (3)
      8     4                                                   (object header)                           df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
......

========== 释放锁之后 ==========
java.lang.Class object internals:
 OFFSET  SIZE                                              TYPE DESCRIPTION                               VALUE
      0     4                                                   (object header)                           01 a4 ba 5f (00000001 10100100 10111010 01011111) (1606067201)
      4     4                                                   (object header)                           13 00 00 00 (00010011 00000000 00000000 00000000) (19)
      8     4                                                   (object header)                           df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
......


实验结论

我们可以观察到对象头信息确实与我们预想的一样,证明:当 synchronized 修饰静态方法时,锁信息是存储在 当前类Class对象头中的

三、当 synchronized(obj) 修饰同步代码块时,锁信息是存储在obj对象头中的

实验思路

  1. 在使用锁之前,锁信息肯定没有存储,这时打印obj对象信息

  2. 在使用锁的时,锁的信息肯定已经存储,这时打印obj对象信息

  3. 在使用锁之后,锁的信息肯定已经消除,这时打印obj对象信息

预想结果

如果第一步和第三步的obj对象头信息相同,第二步与第一、第三步的obj对象头信息不同

则证明对象头信息是存储在obj对象的对象头中

实验代码

public class T001_Synchronized_Code_Block {

    private static Object obj = new Object();

    static void m()  {
        synchronized (obj){
            LockSupport.park();
        }
    }


    public static void main(String[] args) throws InterruptedException {
        System.out.println("========== 加锁之前 ==========");
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());

        System.out.println("========== 加锁中 ==========");
        Thread thread = new Thread(() -> m());
        thread.start();
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());

        LockSupport.unpark(thread);
        System.out.println("========== 释放锁之后 ==========");
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }
}

实验结果

========== 加锁之前 ==========
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

========== 加锁中 ==========
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           a0 89 80 0b (10100000 10001001 10000000 00001011) (192973216)
      4     4        (object header)                           03 00 00 00 (00000011 00000000 00000000 00000000) (3)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

========== 释放锁之后 ==========
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


实验结论

我们可以观察到对象头信息确实与我们预想的一样,证明:当 synchronized(obj) 修饰同步代码块时,锁信息是存储在obj对象头中的


疑问:多线程是否同时可以访问同一个类中 不同 synchronized 修饰的方法?

  1. 当 synchronized 修饰普通方法时,锁信息是存储在 this 对象中头的

  2. 当 synchronized 修饰静态方法时,锁信息是存储在 当前类Class对象头中的

  3. 当 synchronized(obj) 修饰同步代码块时,锁信息是存储在obj对象头中的

如果1锁定的是当前 this 对象,多个线程不可能同时访问 同一个this对象 的任意synchronized修饰的普通方法

如果2锁定的是当前类Class,多个线程不可能同时访问 同一个Class对象 的任意synchronized修饰的静态方法

如果3锁定的是obj,多个线程不可能同时访问任意 synchronized(obj) 修饰的代码块

答案是肯定的可以的

一、如果1锁定的是当前 this 对象,多个线程不可能同时访问 同一个this对象 的任意synchronized修饰的普通方法

如果1锁定的是当前 this 对象,多个线程可以同时访问 不同this对象 的任意 synchronized 修饰的普通方法

实验思路

  1. 线程1 访问 synchronized 修饰的普通方法 m()
  2. 在线程1运行过程中,线程2 访问 synchronized 修饰的普通方法 n()
  3. 在线程1运行过程中,线程3 访问普通方法 o()
  4. 观察线程1和线程2是否同时运行,线程1和线程3 是否同时运行

预想结果

如果线程1和线程2不能同时运行,线程1和线程3可以同时运行,则验证了我们的猜想

实验代码

public class T001_Synchronized_Method_2 {

    synchronized void m()  {
        System.out.println(Thread.currentThread().getName()+"进入方法 m ");
        LockSupport.park();
    }

    synchronized void n()  {
        System.out.println(Thread.currentThread().getName()+"进入方法 n ");
        LockSupport.park();
    }

    void o()  {
        System.out.println(Thread.currentThread().getName()+"进入方法 o ");
        LockSupport.park();
    }


    public static void main(String[] args) throws InterruptedException {
        T001_Synchronized_Method_2 test = new T001_Synchronized_Method_2();

        Thread thread = new Thread(() -> test.m(),"线程1");
        thread.start();
        Thread.sleep(500);

        Thread thread2 = new Thread(() -> test.n(),"线程2");
        thread2.start();

        Thread thread3 = new Thread(() -> test.o(),"线程3");
        thread3.start();
    }
}

实验结果

线程1进入方法 m 
线程3进入方法 o 

实验结论

我们可以观察到线程1 和线程3 正常运行,线程2 却迟迟不见输出,证明:如果锁定的是当前 this 对象,多个线程不可能同时访问 同一个this对象 的任意 synchronized 修饰的普通方法

二、如果2锁定的是当前类Class,多个线程不可能同时访问 同一个Class对象 的任意synchronized修饰的静态方法

实验思路

  1. 线程1 访问 synchronized 修饰的静态方法 m()
  2. 在线程1运行过程中,线程2 访问 synchronized 修饰的静态方法 n()
  3. 在线程1运行过程中,线程3 访问普通静态方法 o()
  4. 观察线程1和线程2是否同时运行,线程1和线程3 是否同时运行

预想结果

如果线程1和线程2不能同时运行,线程1和线程3可以同时运行,则验证了我们的猜想

实验代码

public class T001_Synchronized_Static_Method_2 {

   static synchronized void m()  {
        System.out.println(Thread.currentThread().getName()+"进入方法 m ");
        LockSupport.park();
    }

    static synchronized void n()  {
        System.out.println(Thread.currentThread().getName()+"进入方法 n ");
        LockSupport.park();
    }

    static void o()  {
        System.out.println(Thread.currentThread().getName()+"进入方法 o ");
        LockSupport.park();
    }


    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> m(),"线程1");
        thread.start();
        Thread.sleep(500);

        Thread thread2 = new Thread(() -> n(),"线程2");
        thread2.start();

        Thread thread3 = new Thread(() -> o(),"线程3");
        thread3.start();
    }
}

实验结果

线程1进入方法 m 
线程3进入方法 o 

实验结论

我们可以观察到线程1 和线程3 正常运行,线程2 却迟迟不见输出,证明:如果锁定的是当前类Class,多个线程不可能同时访问 同一个Class对象 的任意 synchronized 修饰的静态方法

三、如果3锁定的是obj,多个线程不可能同时访问任意 synchronized(obj) 修饰的代码块

实验思路

  1. 线程1 访问方法m() 其中包含 synchronized(obj) 修饰的代码块
  2. 在线程1运行过程中,线程2 访问方法n() 其中包含 synchronized(obj) 修饰的代码块
  3. 在线程1运行过程中,线程3 访问普通方法 o()
  4. 观察线程1和线程2是否同时运行,线程1和线程3 是否同时运行

预想结果

如果线程1和线程2不能同时运行,线程1和线程3可以同时运行,则验证了我们的猜想

实验代码

public class T001_Synchronized_Code_Block_2 {

    private static Object obj = new Object();

    void m()  {
        synchronized (obj){
            System.out.println(Thread.currentThread().getName()+"进入方法 m ");
            LockSupport.park();
        }
    }

    static synchronized void n()  {
        synchronized (obj){
            System.out.println(Thread.currentThread().getName()+"进入方法 n ");
            LockSupport.park();
        }
    }

    void o()  {
        System.out.println(Thread.currentThread().getName()+"进入方法 o ");
        LockSupport.park();
    }


    public static void main(String[] args) throws InterruptedException {
        T001_Synchronized_Code_Block_2 test = new T001_Synchronized_Code_Block_2();

        Thread thread = new Thread(() -> test.m(),"线程1");
        thread.start();
        Thread.sleep(500);

        Thread thread2 = new Thread(() -> test.n(),"线程2");
        thread2.start();

        Thread thread3 = new Thread(() -> test.o(),"线程3");
        thread3.start();
    }
}

实验结果

线程1进入方法 m 
线程3进入方法 o 

实验结论

我们可以观察到对象头信息确实与我们预想的一样,证明:如果锁定的是obj,多个线程不可能同时访问任意 synchronized(obj) 修饰的代码块

posted @ 2022-12-04 22:07  Cool_Yang  阅读(57)  评论(0编辑  收藏  举报