synchronized(this)是否仅锁定同步块或所有“this”代码?

时间:2015-07-10 13:51:27

标签: java multithreading synchronized java-threads synchronized-block

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”不应该意味着我不能在构造函数退出synchronized块之前调用getCount()(假设我没有在getCount上写同步) ))。
  3. 如果我在代码中的某个位置执行同步块,它是仅锁定同步块还是所有“this”代码?
  4. 从这里开始编辑:谢谢大家,这非常有帮助,但我还有一些问题可以回答你的问题。
  5. 如果我理解正确,synchronized(this)块不会影响(或连接到)静态同步函数(以锁定术语而不是numOfInstances增量)?
  6. 有一个更好的选项来增加和getCount()函数线程安全吗? (比如打开一个静态对象并执行synchronized(obj)而不是synchronized(this) - 朋友建议)。
  7. 如果我在ObjectCounter类中有一个f1()方法(非静态),而一个线程在同步(this)中,其他线程可以输入f1()块(不是同步类或内部有同步块)吗?
  8. 如果我在ObjectCounter中有f1()方法(非静态)和f2()方法(非静态),在f1()中我有同步(this)块。当一个线程在同步(this)块中时,其他线程是否可以进入f1()块(不是同步类或内部有同步块)? (假设两个线程在同一个实例上“工作”)
  9. `

4 个答案:

答案 0 :(得分:8)

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

由于静态同步块使用与构造函数中的块使用的锁不同的锁,因此进入静态同步块不会被另一个线程访问需要获取当前实例上的锁的块以及同步块阻止在构造函数中对任何东西都没有影响,锁获取将始终是无约束的。更重要的是,这里使用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关键字表示“你必须在输入之前获得一个锁”,假设锁的方法在哪里:方法上的static关键字是类的监视器,没有静态关键字,它是监视当前实例。为了使锁定正常工作,不同的块和方法需要使用相同的锁。可以说有太多的语法糖和太多使得Java的设计方便:允许隐式选择锁并将监视器放在java.lang.Object上会导致混淆。

写下您的问题#6:对于您在这里所做的事情,您最好使用AtomicLong。使用synchronized块来协调需要在不受其他线程干扰的情况下进行的多个更改。

问题#3,#7和#8看起来非常相似:如果方法/块没有尝试获取锁,则没有什么能阻止线程执行该方法/块。对象作为一个整体没有得到任何保护,使用同步方法或块来强制执行锁定是什么保护。在“使用synchronized关键字”方面少考虑一下,更多地考虑锁线程需要获取的内容。

答案 1 :(得分:2)

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

  2. 是的,这是正确的。只能有一个线程同时锁定一个对象。

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

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

答案 2 :(得分:0)

synchronized步骤:

  1. 检查是否已获取对象锁定。如果是,请进入synchronized block / method
  2. 尝试获取锁定。如果锁已经被另一个线程获取,那么线程将等待锁被释放,此时它将再次经历循环(2.)

答案 3 :(得分:-2)

  

代码中的两个选项有区别吗?

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

  

如果我锁定"这个"不应该意味着我无法调用getCount()直到   承包商退出同步块(假设我不写   在getCount()上同步。

由于对象只有一个锁(类锁是不同的,通过使用static关键字和synchronized来保持),所以如果其他一些线程因为{而获取该锁定{1}}或者由于这个synchronized(this){,尝试获取锁的新线程必须等到前一个线程释放锁。

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

  

如果我在代码中的某个地方执行同步块,它是否锁定   唯一的同步块或所有"这个"码?

  1. 非静态同步: 如果你在代码中的某个地方进行了同步块并且它是非静态的getCount(),那么你的对象的锁也将被保留,所以试图获取锁的新线程必须等到前一个线程有释放了锁。

  2. 静态同步: 如果你在代码中的某个地方执行了synchronized块并且它是静态public synchronized long getCount(){,那么它对锁定非静态同步没有影响。


  3. 底线:

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

    • 然后有一个类锁定,如果public static synchronized long getCount(){关键字与static关键字一起使用,则会保留。