189

如果我在同一个类上同步两个方法,它们可以在同一个对象上同时运行吗?例如:

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

我知道我不能methodA()在两个不同的线程中对同一个对象运行两次。同样的事情methodB()

但是我可以在仍在运行的methodB()同时在不同的线程上运行吗?methodA()(同一个对象)

4

12 回答 12

175

两种方法都锁定同一个监视器。因此,您不能同时在不同线程的同一个对象上执行它们(两种方法之一将阻塞,直到另一个完成)。

于 2013-03-15T17:36:33.783 回答
136

在示例中 methodA 和 methodB 是实例方法(与静态方法相反)。使用synchronized实例方法意味着线程必须在调用该方法的对象实例上获取锁(“内在锁”),然后线程才能开始执行该方法中的任何代码。

如果您有两个不同的实例方法标记为同步,并且不同的线程在同一个对象上同时调用这些方法,那么这些线程将竞争同一个锁。一旦一个线程获得锁,所有其他线程都将被该对象上的所有同步实例方法关闭。

为了使这两种方法同时运行,它们必须使用不同的锁,如下所示:

class A {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized(lockA) {
            //method A
        }
    }

    public void methodB() {
        synchronized(lockB) {
            //method B
        }
    }
}

其中同步块语法允许指定执行线程需要获取内部锁定才能进入块的特定对象。

需要理解的重要一点是,即使我们在各个方法上加上了“同步”关键字,核心概念是幕后的内在锁。

以下是Java 教程描述这种关系的方式:

同步是围绕称为内在锁或监视器锁的内部实体构建的。(API 规范通常将此实体简称为“监视器”。)内在锁在同步的两个方面都发挥作用:强制对对象状态的独占访问和建立对可见性至关重要的先发生关系。

每个对象都有一个与之关联的内在锁。按照惯例,需要对对象字段进行排他和一致访问的线程必须在访问对象之前获取对象的内在锁,然后在完成访问时释放内在锁。在获得锁和释放锁之间,线程被称为拥有内在锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。另一个线程在尝试获取锁时会阻塞。

锁定的目的是保护共享数据。仅当每个锁保护不同的数据成员时,您才可以使用上面示例代码中所示的单独锁。

于 2013-03-15T17:39:23.983 回答
20

Java线程进入实例同步java方法时获取对象级锁,进入静态同步java方法时获取类级锁

在您的情况下,方法(实例)属于同一类。因此,当一个线程进入 java 同步方法或阻塞时,它会获取一个锁(调用该方法的对象)。因此,在第一个方法完成并释放 lock(on object) 之前,不能在同一对象上同时调用其他方法。

于 2013-03-15T17:47:10.157 回答
14

在您的情况下,您在同一个类实例上同步了两个方法。所以,这两个方法不能同时在同一个A类实例的不同线程上运行。但是它们可以在不同的A类实例上运行。

class A {
    public synchronized void methodA() {
        //method A
    }
}

是相同的:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}
于 2013-03-15T17:49:10.227 回答
10

将您的代码视为以下代码:

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

public void methodB() {
    synchronized(this){
      // method B body
    }
}

因此,方法级别的同步只是意味着同步(this)。如果任何线程运行这个类的方法,它会在开始执行之前获得锁,并持有它直到方法执行完成。

但是我可以在 methodA() 仍在运行时在不同的线程上运行 methodB() 吗?(同一个对象)

确实,这是不可能的!

因此,多个线程将无法同时在同一个对象上运行任意数量的同步方法。

于 2018-01-05T02:18:16.803 回答
7

来自 oracle 文档链接

使方法同步有两个效果:

首先,同一对象上的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他为同一对象调用同步方法的线程都会阻塞(暂停执行),直到第一个线程处理完该对象。

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

这将回答您的问题:在同一个对象上,当第一个同步方法执行正在进行时,您不能调用第二个同步方法。

查看此文档页面以了解内在锁和锁定行为。

于 2016-02-21T10:06:21.843 回答
4

只是为了清楚起见,静态同步和非静态同步方法都可能同时或并发运行,因为一个具有对象级锁和其他类级锁。

于 2014-07-13T13:55:07.593 回答
3

同步的关键思想不容易陷入困境,它只有在同一个对象实例上调用方法时才会生效- 它已经在答案和评论中突出显示 -

下面的示例程序是为了清楚地指出相同的 -

public class Test {

public synchronized void methodA(String currentObjectName) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}

public synchronized void methodB(String currentObjectName)  throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}

public static void main(String[] args){
    Test object1 = new Test();
    Test object2 = new Test();
    //passing object instances to the runnable to make calls later
    TestRunner runner = new TestRunner(object1,object2);
    // you need to start atleast two threads to properly see the behaviour
    Thread thread1 = new Thread(runner);
    thread1.start();
    Thread thread2 = new Thread(runner);
    thread2.start();
}
}

class TestRunner implements Runnable {
Test object1;
Test object2;

public TestRunner(Test h1,Test h2) {
    this.object1 = h1;
    this.object2 = h2;
}

@Override
public void run() {
    synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
    //noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}

// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
    try {
        object1.methodA("object1");
        object1.methodB("object1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
    try {
        object1.methodA("object1");
        object2.methodB("object2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

请注意,如果在不同的对象实例上调用方法,则如何按预期允许同时访问的输出差异。

带有noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects() 注释的输出 - 输出顺序为 methodA in > methodA Out .. methodB in > methodB Out 输出带有 *noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects()* 的评论

和带有synchronizedEffectiveAsMethodsCalledOnSameObject()的输出 注释- 输出显示 Thread1 和 Thread0 在突出显示的部分同时访问 methodA -

输出带有 *synchronizedEffectiveAsMethodsCalledOnSameObject()* 的评论

增加线程数将使其更加明显。

于 2019-04-08T18:51:56.227 回答
2

不,这是不可能的,如果可能的话,那么两种方法都可以同时更新同一个变量,这很容易破坏数据。

于 2019-01-14T22:44:22.710 回答
1

您正在将它同步到对象而不是类上。所以他们不能同时在同一个对象上运行

于 2013-03-15T17:37:33.860 回答
0

两个不同的线程在同一个对象上执行一个共同的同步方法,由于对象是相同的,当一个线程使用同步方法时,它必须验证锁,如果锁被启用,这个线程将进入等待状态,如果锁被禁用,那么它可以访问对象,而它将访问它将启用锁,并且只有在它执行完成时才会释放锁。当另一个线程到达时,它将验证锁,因为它已启用,它将等待第一个线程完成执行并释放对象上的锁,一旦锁被释放,第二个线程将获得对该对象的访问权并且它将启用锁,直到它被执行。所以执行不会不是并发的,两个线程都会一一执行,

于 2017-02-07T19:51:46.753 回答
0

是的,它们可以同时运行两个线程。如果您创建该类的 2 个对象,因为每个对象仅包含一个锁,并且每个同步方法都需要锁。因此,如果您想同时运行,请创建两个对象,然后尝试使用这些对象引用运行。

于 2019-05-23T10:42:02.570 回答