首页 > 其他分享 >Synchronized相关问题

Synchronized相关问题

时间:2022-12-04 22:25:35浏览次数:35  
标签:00 synchronized Synchronized 00000000 object Thread 问题 线程 相关

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 对象,是不是同一时间内,多个线程不可能同时访问不同 synchronized 修饰的普通方法

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

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

答案是肯定的

一、如果1锁定的是当前 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 对象,多个线程不可能同时访问不同 synchronized 修饰的普通方法

二、如果2锁定的是当前类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,多个线程不可能同时访问不同 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) 修饰的代码块

标签:00,synchronized,Synchronized,00000000,object,Thread,问题,线程,相关
From: https://www.cnblogs.com/coolyang/p/16950973.html

相关文章

  • MapStruct与lombok加载顺序问题与annotationProcessorPaths的关系?
    MapStruct是什么?MapStructisacodegeneratorthatgreatlysimplifiestheimplementationofmappingsbetweenJavabeantypesbasedonaconventionoverconfigu......
  • 使用Windbg找出程序CPU高问题
    使用Windbg找出程序CPU高问题ReggieDing系统架构师​关注 1人赞同了该文章背景本人在把应用程序部署到服务器上运行,观察一段时间后运行平稳,CPU......
  • WSL2代理问题
    在使用WSL1时,由于Linux子系统和Windows共享网络端口,所以在Windows开代理时,Linux子系统也可以共享,但是WSL2使用虚拟化运行Linux内核,也有自己独立的网络端口,所以无法像WSL1一......
  • 域名相关
    域名解析阿里云域名设置URLNginx配置worker_processes1;events{worker_connections1024;}http{includemime.types;default_type......
  • VSCode外部终端中文乱码问题
    转载自:https://www.cnblogs.com/stu-jyj3621在处理这个问题之前,你首先得知道为什么会出现这个这个问题。你在使用VScode编辑代码时,代码页面中文正常,而终端输出那里中文......
  • 小程序上传问题
    1.80200,mainpackagesourcesize2590KBexceedmaxlimit2MB要求每个分包不能大于2MB一般就是静态资源太大,放在服务器上即可2.background真机调试图片不显示把图......
  • CSS基础-选择器进阶,背景相关属性(颜色/图片)
    CSS基础-选择器进阶,背景相关属性(颜色/图片)目标:能够理解复合选择器的规则,并使用复合选择器在HTML中选择元素学习路径: 1.复合选择器 2.并集选择器 3.交......
  • Request_获取请求参数中文乱码问题处理以及请求转发
    Request_获取请求参数中文乱码问题处理中文乱码问题:get方式:tomcat8已经将get方式乱码问题解决了post方式:会乱码解决:在获取参数前,设置request的编码:r......
  • redis之缓存问题
    一缓存穿透1什么是缓存穿透缓存穿透是指查询一个在redis和DB中都不存在的数据,redis中查不到去DB查,DB查不到则不写入redis,导致每次查询这个数据都要穿过redis穿透到DB......
  • sql题解--打折日期交叉问题
    题目-打折日期交叉问题现有各品牌优惠周期表(promotion_info)如下,其记录了每个品牌的每个优惠活动的周期,其中同一品牌的不同优惠活动的周期可能会有交叉。promotion_id......