如何理解java中synchronized的三种用法

初学者真的不理解


class X {
  // 修饰非静态方法
  synchronized void foo() {
    // 临界区
  }
  // 修饰静态方法
  synchronized static void bar() {
    // 临界区
  }
  // 修饰代码块
  Object obj = new Object();
  void baz() {
    synchronized(obj) {
      // 临界区
    }
  }
}  

我们都知道,第一种锁的是方法
第二种所的是类
第三种所的是对象
这三种锁它们的区别到底是什么呢?锁对象和锁类两个作用有啥区别啊,该怎么理解?

其实很好理解
静态方法是在类加载的时候被初始化到方法区,方法区是共享区域。也就意味着jvm运行期间,你所有的调用都是调用同一个方法。(所以这个锁全局有效)
而非静态修饰的方法是需要用对象实例调用,就是你对象创建一般是new指令创建新的对象。(所以这个锁只对此对象实例调用的方法生效,重新创建对象实例后不再生效)
对象的创建是通过在堆内存里分配空间完成的,方法帧中存在的局部变量表对应的其实也就是该对象实例在内存中的地址(简单理解)所以这个锁是判断对象实例是不是同一个地址如果是的话就互斥,不是的话就非互斥

下面是解释,你可以参考,希望对你有帮助。

  1. 修饰非静态方法

    img


    当一个线程进入该方法时,它会尝试获取当前实例的锁。如果获取成功,该线程就可以执行临界区代码,直到退出该方法并释放锁为止。如果获取失败,则该线程就会被阻塞,直到锁被释放。需要注意的是,对于不同的实例,它们的锁是互不干扰的
  2. 修饰静态方法

    img


    当一个线程进入该方法时,它会尝试获取当前类的锁。如果获取成功,该线程就可以执行临界区代码,直到退出该方法并释放锁为止。如果获取失败,则该线程就会被阻塞,直到锁被释放。需要注意的是,对于不同的实例,它们共享同一个类锁。
  3. 修饰代码块

    img


    当一个线程进入该代码块时,它会尝试获取obj对象的锁。如果获取成功,该线程就可以执行临界区代码,直到退出代码块并释放锁为止。如果获取失败,则该线程就会被阻塞,直到锁被释放。需要注意的是,不同的线程可以同时进入不同的同步代码块,因为它们互不干扰。

**锁对象和锁类两个作用有啥区别__**在Java中,synchronized关键字可以修饰实例方法和静态方法。当修饰实例方法时,锁对象是实例对象本身;当修饰静态方法时,锁对象是类对象。

锁对象和锁类分别具有不同的作用和特点:(这个百度里面有好多的帖子,可以找一下)

  1. 锁对象:
    锁对象主要用于对实例方法的同步控制。当一个线程进入一个synchronized修饰的实例方法时,它会尝试获取该实例对象的锁,如果获取成功,则其他线程无法进入该实例对象的其他synchronized方法或代码块,直到当前线程执行完临界区代码并释放锁为止。不同的实例对象之间的锁是互不干扰的,因此,不同的线程可以同时进入不同的实例对象的synchronized方法或代码块。
  2. 锁类:
    锁类主要用于对静态方法的同步控制。当一个线程进入一个synchronized修饰的静态方法时,它会尝试获取该类的锁,如果获取成功,则其他线程无法进入该类的其他synchronized静态方法或代码块,直到当前线程执行完临界区代码并释放锁为止。不同的实例对象共享同一个类锁,因此,不同的线程无法同时进入同一个类的synchronized静态方法或代码块。

因此,锁对象和锁类的主要区别在于锁对象用于对实例方法的同步控制,锁类用于对静态方法的同步控制。需要根据具体情况选择合适的锁方式。

其实都有一个共同点:都是对某个对象加锁。
1.修饰非静态方法 是对调用该方法的对象加锁。

X x = new X();
x.foo();
// 这里就是对x对象加锁,其他线程调用x.foo()时就会被卡住

2.修饰静态方法 是对该静态方法的类加锁,每个类在JVM中都有一个描述其结构的Class对象。

X x = new X();
x.bar();//这里对X.class对象加锁
x.foo();//其他线程调用这里会卡住,因为X.class已经在上一步被占用了

3.修饰代码块 是对自定义的对象加锁,灵活性比上面两种都要好

Object obj = new Object();
  void baz() {
    synchronized(obj) {
      // 临界区
    }
  }
Object obj2 = new Object();
  void baz2() {
    synchronized(obj2) {
      // 临界区
    }
  }
// 这两个方法加锁的对象不同,那么这两个方法的执行就是互不干扰的
不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^