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  阅读(89)  评论(0)    收藏  举报