5
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 类中有一个 f1() 方法(非静态),而一个线程在 synchronized(this) 中,其他线程可以进入 f1() 块(不是同步类或内部有同步块)吗?
  8. 如果我在 ObjectCounter 中有 f1() 方法(非静态)和 f2() 方法(非静态),则在 f1() 中我有 synchronized(this) 块。当一个线程在 synchronized(this) 块中时,其他线程可以进入 f1() 块(不是同步类或内部有同步块)吗?(假设两个线程在同一个实例上“工作”)

`

4

4 回答 4

9

使用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 关键字是类上的监视器,没有 static 关键字它是类上的监视器当前实例。为了使锁定正常工作,不同的块和方法需要使用相同的锁。可以说,Java 的设计方式有太多的语法糖和太多的便利:允许隐式选择锁并将监视器放在 java.lang.Object 上可能会导致混乱。

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

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

于 2015-07-10T13:56:40.280 回答
2
  1. 是的,选项有所不同。在上面的选项中,两个线程不能同时调用getCount(),在下面的选项中它们可以。

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

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

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

于 2015-07-10T13:59:08.297 回答
0

synchronized脚步:

  1. 检查是否已获取对象锁。如果是这样,进入同步块/方法
  2. 尝试获取锁。如果锁已经被另一个线程获取,那么该线程将等待锁被释放,此时它将再次经历循环(2.)
于 2015-07-10T14:00:19.550 回答
-2

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

是的,有明显的区别。getCount()首先,您正在同步线程访问ObjectCounter. 而在第二个你不是。

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

由于一个对象只有一个锁(类锁是不同的,通过using 关键字static与等到前一个线程释放了锁。synchronizedsynchronized(this){synchronized long getCount(){

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

如果我在代码中的某个位置执行同步块,它会锁定唯一的同步块还是所有“this”代码?

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

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


底线:

  • 一个对象只有一个锁,如果某个线程获取了该锁,则其他线程必须等待该锁释放。

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

于 2015-07-10T13:57:44.493 回答