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

时间:2013-03-15 17:35:34

标签: java multithreading synchronized java-threads

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

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

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

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

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

12 个答案:

答案 0 :(得分:116)

两种方法都锁定同一台显示器。因此,您无法在不同线程的同一对象上同时执行它们(两种方法中的一种将阻塞,直到另一方完成)。

答案 1 :(得分:90)

在示例中,methodA和methodB是实例方法(与静态方法相对)。将synchronized放在实例方法上意味着线程必须获取对象实例上的锁(“内部锁”),该方法在线程可以开始执行该方法中的任何代码之前调用该方法。

如果有两个不同的实例方法标记为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
        }
    }
}

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

要理解的重要一点是,尽管我们在个别方法上放置了“synchronized”关键字,但核心概念是幕后的内在锁定。

以下是the Java tutorial描述这种关系的方式:

  

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

     

每个对象都有一个与之关联的内在锁。按照惯例,需要对对象字段进行独占和一致访问的线程必须在访问对象之前获取对象的内部锁,然后在完成它们时释放内部锁。据说一个线程在获得锁定和释放锁定之间拥有内在锁定。只要一个线程拥有一个内部锁,没有其他线程可以获得相同的锁。另一个线程在尝试获取锁时会阻塞。

锁定的目的是保护共享数据。只有在每个锁保护不同的数据成员时,才会使用上面示例代码中所示的单独锁。

答案 2 :(得分:14)

Java Thread在进入 实例 synchronized java方法并获取类级锁定<时获取对象级锁定 / strong>进入 静态 同步java方法时。

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

答案 3 :(得分:10)

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

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

与:

相同
class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}

答案 4 :(得分:7)

来自oracle文档link

使方法同步有两个影响:

  

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

     

其次,当synchronized方法退出时,它会自动与同一对象的同步方法的任何后续调用建立一个before-before关系。这可以保证对所有线程都可以看到对象状态的更改

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

查看本文档page以了解内部锁和锁定行为。

答案 5 :(得分:5)

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

class A {

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

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

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

  

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

确实,这是不可能的!

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

答案 6 :(得分:4)

一目了然,静态同步和非静态同步方法可以同时或同时运行,因为一个具有对象级别锁定和其他类级别锁定。

答案 7 :(得分:1)

您正在对象上进行同步,而不是在课堂上进行同步。所以他们不能同时在同一个对象上运行

答案 8 :(得分:1)

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

答案 9 :(得分:0)

具有同步功能的想法很难实现,即只有在相同对象实例上调用方法时,它才会生效-已经突出显示了在答案和评论中-

下面的示例程序将明确指出相同的内容-

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 Ouput with *noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects()* commented

和带有 synchronizedEffectiveAsMethodsCalledOnSameObject()的Ouput 评论- 输出在突出显示的部分-

中显示了Thread1和Thread0同时访问methodA

Ouput with *synchronizedEffectiveAsMethodsCalledOnSameObject()* commented

增加线程数将使其更加引人注目。

答案 10 :(得分:0)

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

答案 11 :(得分:-1)

两个不同的线程在单个对象上执行公共同步方法,因为对象是相同的,当一个线程使用同步方法时,它必须变量锁,如果锁被启用,这个线程将转到等待状态,如果锁定被禁用,那么它可以访问该对象,而它将访问它将启用锁定并将释放锁定 只有当它的执行完成时。 当另一个线程到达时,它将改变锁定,因为它被启用它将等待第一个线程完成执行并释放对象的锁定,一旦锁定被释放,第二个线程将获得对象的访问权限它将启用锁定直到它的执行。 因此执行不会是并发的,两个线程将逐个执行,当两个线程在不同的对象上使用synchronized方法时,它们将同时运行。