为什么我们需要为synchronized语句指定锁?

时间:2016-12-23 06:47:51

标签: java concurrency synchronized

鉴于每个类的实例只有一个锁,那么为什么Java不允许我们这样做:

void method() {
    synchronized {
        // do something
    }

    // do other things
}

而不是:

void method() {
    synchronized (lock) {
        // do something
    }

    // do other things
}

指定锁定的目的是什么?如果我选择一个对象作为锁定而不是另一个对象会有所不同吗?或者我可以选择任何随机对象吗?

编辑:

事实证明,我对同步方法的理解在基础层面上是错误的。

我认为不管锁是什么,不同的同步方法或块完全相互独立。相反,具有相同锁的所有同步方法或块只能由一个线程访问,即使这样的同步方法/块来自不同的类(文档应该更多地强调这一点:所有同步方法/块,无论位置如何,重要的是锁定。)

5 个答案:

答案 0 :(得分:3)

  

鉴于每个类的实例只有一个锁,那么为什么Java不允许我们这样做:

void method() {
    synchronized {
        // do something
    }

    // do other things
}

虽然每个实例都提供了内部锁定, 这不一定是"显而易见的"锁定使用。

您可能已经提供了synchronized { ... }作为synchronized (this) { ... }的简写,这可能是正确的。 我不知道为什么他们没有,但我从来没有错过它。 但并发编程很棘手, 因此,使锁定对象成为明确的必需参数可能会使读者更清楚,这是一件好事,正如@ajb在评论中指出的那样。 无论如何,我不认为语法是你的主要问题,所以让我们继续前进。

  

指定锁定的目的是什么?

嗯,锁可能是同步机制中最重要的一件事。同步的关键点是只有一个线程可以保持相同的锁。持有不同锁的两个线程不同步。因此,了解锁定同步的锁是至关重要的。

  

如果我选择一个对象作为另一个对象的锁定,它会有所不同吗?

我希望上一节明确表示是的,你必须仔细选择对象。它必须是所有涉及的线程都可见的对象, 它必须不是null,并且它必须是在同步期间不会被重新分配的东西。

  

或者我可以选择任何随机物体吗?

当然不是。见上一节。

要理解Java中的并发性,我建议API的一位作者使用本书Java Concurrency in Practice,或者Oracle's tutorials来讨论该主题。

答案 1 :(得分:1)

这样你就可以锁定与this完全不同的东西。

还记得Vector是如何"线程安全的?"它并不那么简单;每个调用都是,但是这样的代码并不是因为它可以在获取向量的大小和获取元素之间进行更新:

for (int i = 0; i < vector.size(); ++i) System.out.println(vector.get(i));

由于VectorCollections.synchronized*一起与旧的synchronized关键字同步,因此您可以将上述代码全部封闭在锁定中,从而使上述代码具有线程安全性:

synchronized (vector) {
    for (int i = 0; i < vector.size(); ++i) System.out.println(vector.get(i));
}

这可能是一种线程安全,不同步或使用ReentrantLock的方法;锁定向量与锁定this分开。

答案 2 :(得分:1)

它肯定会对你用作锁的对象产生影响。如果你说

void method() {
    synchronized (x) {
        // do something
    }

    // do other things
}

现在,如果一个线程正在执行该块而另一个线程尝试进入该块,如果两个线程的x相同,那么第二个线程将不得不等待。但是如果x不同,则第二个线程可以同时执行该块。因此,例如,如果method是一个实例方法而你说

void method() {
    synchronized (this) {
        // do something
    }    
    // do other things
}

现在,使用同一对象运行该方法的两个线程无法同时执行该块,但是两个线程仍然可以在不同对象上运行该方法而不会相互阻塞。当您想要阻止同时访问该对象中的实例变量时,这就是您想要的,但您没有其他任何需要保护的内容。如果两个线程正在访问两个不同对象中的变量,那不是问题。

但是说代码块正在访问公共资源,并且您希望确保所有其他线程都无法访问该资源。例如,您正在访问数据库,并且该块执行一系列更新,并且您希望确保它们以原子方式完成,即当您处于两次更新之间时,其他代码不应访问数据库。现在synchronized (this)还不够好,因为你可以为两个不同的对象运行该方法但访问同一个数据库。在这种情况下,您需要一个对可能访问同一数据库的所有对象都相同的锁。在这里,使数据库对象本身成为锁是可行的。现在没有两个线程可以使用method同时进入此块,如果它们使用相同的数据库,即使对象不同。

答案 3 :(得分:0)

synchronized (lock)..中,lock可以是对象级锁,也可以是类级锁。

  • Example1 Class Level Lock:

    private static Object lock=new Object();
    synchronized (lock){
    //do Something
    }
    
  • Example2对象级别锁定:

    private Object lock=new Object();
    synchronized (lock){
    //do Something
    }
    

答案 4 :(得分:0)

如果你有多个对象,b1 / b2需要更新并发

class A {
    private B b1, b2;
}

如果您只有一个锁,则说A类本身

synchronized (this) { ... }

然后假设有两个线程在同一时间更新b1和b2,它们将逐个播放,因为 synchronized(this)

但如果b1和b2有两个锁

private Object lock1 = new Object, lock2 = new Object;

我提到的两个主题将同时播放,因为 synchronized(lock1)不会影响 synchronized(lock2)。有时候意味着更好的性能。