【问题标题】:Synchronized block will lock the whole object or the method alone?同步块会锁定整个对象还是单独锁定方法?
【发布时间】:2017-07-07 06:21:49
【问题描述】:

我在一个类中有多个方法,并且大多数方法都有关键部分(共享数据)。所以我将这些方法设为同步。假设线程 t1 正在运行同步块之一。同时线程 t2 可以访问其他方法的临界区吗?

class Sample{

synchronized public void method1(){

}

synchronized public void method2(){

}

synchronized public void method3(){

}

public void method4(){

}

}

【问题讨论】:

  • 整个对象
  • @JavaUser 假设:所有线程都是用相同的示例对象创建的 答案:如果调用了任何同步方法(方法 1 或方法 2 或方法 3),那么所有其他同步方法将保持阻塞,直到执行完成完全的 。方法 4 永远不会得到 block ,无论情况如何。
  • 相反,如果您有两个相同类型的对象Sample s1 = new Sample(); Sample s2 = new Sample();,那么当在s1 上调用同步方法时,只有s1 被锁定,而不是s2。这应该是显而易见的,但有时人们会忘记这是有区别的。
  • Java 中的同步实例方法在拥有该方法的实例(对象)上同步。因此,每个实例都有其同步方法在不同的对象上同步:拥有的实例。只有一个线程可以在同步实例方法中执行。如果存在多个实例,则一次可以在每个实例的同步实例方法中执行一个线程。每个实例一个线程。见tutorials.jenkov.com/java-concurrency/…
  • 如果synchronized方法是static,那么方法所有者和该块的监视器是Class,所以整个类都被锁定了。

标签: java multithreading thread-safety synchronized


【解决方案1】:

同步总是锁定一个对象。在同步方法的情况下,对象是this。所以基本上这两种方法都是一样的:

synchronized public void method1() {
  // do something
}

public void method1() {
  synchronized(this){
    // do something
  }
}

只要一个线程拥有锁对象的锁,其他线程就不能锁定这个对象。因此,在您的示例中,同步方法(一、二和三)永远不能同时执行。 method4不同步,所以可以随时访问对象。

如果您想要更细粒度的锁定,因为 method1method2 应该是独占的,而 method3method4 您可以使用如下示例:

class Sample{
  private final Object lock1 = new Object();
  private final Object lock2 = new Object();

  public void method1(){
    synchronized(lock1) {
      // do something
    }
  }
  public void method2(){
    synchronized(lock1) {
      // do something
    }
  }

  public void method3(){
    synchronized(lock2) {
      // do something
    }
  }
  public void method4(){
    synchronized(lock2) {
      // do something
    }
  }
}

然后您甚至可以使用synchonized(lock) 方法只包装需要同步的语句,而不是整个方法:

public void method() {
  // some code
  synchronized(lock) {
    // code that must be synchronized
  }
  // some other code
}

通过这种方法,您可以将锁定持续时间保持在最短。

【讨论】:

  • 这是正确的,但需要注意的是,通常应避免同步。这是一种归档线程安全的粗暴方法,在大多数情况下并发类更适合该任务。
  • @TwoThe:我完全同意,我更喜欢不可变对象、基于事件和/或基于流的架构等,但这是另一回事。
  • 可能还应该注意synchronized static方法锁定在类的Class对象上。
  • @TwoThe,synchronized 到底有多野蛮?它简单、安全、有效。 java.util.concurrent.locks 提供了更强大的替代方案,但更多的权力意味着更少的安全性。如果您在谈论java.util.concurrent 中的其他类(例如,线程安全队列、映射等),那么当其中一个解决问题时重新发明它们当然是愚蠢的,但它们并不能解决所有问题问题。
  • synchronized 也是独占的,这意味着它将所有线程搁置以仅服务单个线程。在许多大大降低应用程序吞吐量的情况下,在最坏的情况下,它会将它们转回单线程。正确使用并发类通常可以完全避免这些缺点。
【解决方案2】:
  • 同一对象上的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他为同一对象调用同步方法的线程都会阻塞(暂停执行),直到第一个线程处理完该对象。

  • 当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立之前发生的关系。这保证了对象状态的变化对所有线程都是可见的

【讨论】:

    【解决方案3】:

    在这种情况下

    Sample sample1 = new Sample();
    

    多线程无法访问

    sample1.method1(),sample1.method2(),sample1.method3()
    

    但是多个线程可以同时访问

    sample1.method4()
    

    如果我们有 2 个对象

    Sample sample1 = new Sample();
    Sample sample2 = new Sample();
    

    多线程可以并发访问

    sample1.method1(),sample2.method1() etc.
    

    所以锁在对象级别

    【讨论】:

      猜你喜欢
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-07
      • 1970-01-01
      相关资源
      最近更新 更多