synchronized (this) 是只锁定同步块还是锁定所有 "this" 代码?

Does synchronized (this) lock only the synchronized block or all the "this" code?

public class ObjectCounter {
    private static long numOfInstances = 0;
    public ObjectCounter(){
        synchronized(this){
        numOfInstances++;
        }
    }
    **public static synchronized long getCount(){
        return numOfInstances;
    }**
//vs//
    **public static long getCount(){
        return numOfInstances;
    }**
}

如果我 运行 几个线程,它们中的一些调用静态函数 getCount() 并且它们中的一些创建新实例。我想在每次调用 getCount() 时获取实际实例数。

  1. 代码中的两个选项有区别吗?
  2. 如果我锁定“this”,这是否意味着我不能调用 getCount() 直到构造函数退出同步块(假设我不在getCount()).
  3. 如果我在代码的某个地方做同步块,它是只锁定同步块还是锁定所有“this”代码?
  4. 从这里开始编辑:谢谢大家,这很有帮助,但在您的回答之后我还有几个问题。
  5. 如果我理解正确,synchronized(this) 块不会影响(或连接到)静态同步函数(锁定术语不是 numOfInstances 增量)?
  6. 是否有更好的选择使增量和 getCount() 函数线程安全? (比如打开一个静态对象并执行 synchronized(obj) 而不是 synchronized(this) - 朋友建议)。
  7. 如果我在 ObjectCounter class 中有一个 f1() 方法(非静态),而一个线程处于同步 (this) 中时,其他线程是否可以进入 f1() 块(不是同步 class 或者里面有同步块)?
  8. 如果我在 ObjectCounter 中有 f1() 方法(非静态)和 f2() 方法(非静态),在 f1() 中我有 synchronized(this) 块。当一个线程在 synchronized(this) 块中时,其他线程是否可以进入 f1() 块(不是同步 class 或内部有同步块)? (可以说同一个实例上的两个线程 "working")

`

使用synchronized 意味着为了让线程执行该块或方法,它必须获取该块或方法引用的锁(显式或隐式)。对于 static synchronized 方法,该锁是 class 对象上的监视器。对于 synchronized(this) 块,使用的锁是当前实例上的监视器。在多个方法或块之间共享锁是强制更新的原子性和内存可见性的原因,共享锁还提供了一个共享的通信路径,通过该路径可以进行等待和通知。

由于静态同步块使用的锁与构造函数中的块使用的锁不同,因此进入静态同步块不会被另一个线程访问需要获取当前实例锁的块所阻塞,并且构造函数中的 synchronized 块对任何东西都没有影响,锁的获取将始终是无竞争的。在这里更重要的是,一个线程在构造函数中所做的更改可能不会被使用 getter 的其他线程看到。同步会影响锁定和内存可见性。

这个更改后的版本可以工作:

public class ObjectCounter {
    private static long numOfInstances = 0;
    public ObjectCounter(){
        synchronized(ObjectCounter.class){
            numOfInstances++;
        }
    }
    public static synchronized long getCount(){
        return numOfInstances;
    }
}

因为getter和递增块使用同一个锁。让不同的线程获取相同的监视器可确保安全地发布对计数器的更改,以便访问 getter 的另一个线程可以看到更新后的值。

synchronized 关键字表示,"you have to acquire a lock before you can enter",假定方法锁定的位置:在方法上使用 static 关键字时,它是 class 上的监视器,没有 static 关键字时,它是监视器在当前实例上。为了使锁定正常工作,不同的块和方法需要使用相同的锁。 Java 的设计方式可以说有太多的语法糖和太多方便的东西:允许隐式选择锁并将监视器放在 java.lang.Object 上会导致混淆。

关于你的问题 #6:对于你在这里所做的事情,你最好使用 AtomicLong。使用同步块来协调需要在不受其他线程干扰的情况下发生的多个更改。

问题#3、#7 和#8 看起来非常相似:如果 method/block 没有尝试获取锁,则没有什么可以阻止线程执行 method/block。对象作为一个整体没有得到任何保护,使用同步方法或块来强制锁定才是保护。少考虑"using the synchronized keyword",多考虑线程需要获取什么。

Is there a difference between the two options in the code?

是的,有明显的区别。首先,您要同步线程对 ObjectCounter 的 class 对象上的 getCount() 方法的访问。而在第二你不是。

If I lock "this" shouldn't it means that I can't call getCount() until the contractor exit the synchronized block (lets say if I don't write synchronize on the getCount()).

因为一个对象只有一个锁(class锁是不同的,它们是通过使用static关键字和synchronized来持有的),所以如果其他线程正在获取由于 synchronized(this){ 或由于此 synchronized long getCount(){ 而导致的锁定,则尝试获取锁定的新线程必须等到前一个线程释放了锁定。

现在因为在你的情况下,你正在做,static synchronized long getCount(){,所以,它的锁定变得不同于 synchronized(this){。这意味着,如果某个线程由于 synchronized(this){ 而正在获取锁,而其他线程正试图调用 getCount(),那么该线程将不会被阻塞。

if I do a synchronized block in some place in the code, does it locks the only the synchronized block or all the "this" code?

  1. 非静态同步: 如果你在代码的某个地方做同步块并且它是非静态的public synchronized long getCount(){,那么你的对象的锁也会被持有,所以试图获取锁的新线程必须等到前一个线程已经释放了锁

  2. 静态同步: 如果你在代码的某个地方做了synchronized block,而且是静态的public static synchronized long getCount(){,那么对非静态同步的锁是没有影响的


底线:

  • 只有 一个对象只有一个锁,如果该锁被某个线程获取,那么 其他线程必须等待 直到锁被释放。

  • 然后有一个class锁,如果static关键字与synchronized关键字一起使用,则持有该锁。

  1. 是的,选项有所不同。在上面的选项中,两个线程不能同时调用 getCount(),在下面的选项中可以。

  2. 是的,没错。同一时间只能有一个线程持有一个对象的锁。

  3. 每个对象都有自己的锁。因此它锁定了该对象的所有 synchronized (this) 块。

但是请注意,每个对象都有自己的锁,每个 class 也都有自己的锁。在构造函数中,您使用对象锁来访问静态 (class) 变量,而在 getCount() 中,您使用 class 锁。这意味着您的代码不是线程安全的!

synchronized 步数:

  1. 检查对象锁是否已经获取。如果是这样,继续同步 block/method
  2. 尝试获取锁。如果锁已经被另一个线程获取,那么该线程会等待锁被释放,此时会再次经历循环(2.)