如果我同步了同一个类上的两个方法,它们能同时运行吗?

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

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


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

我知道我不能在两个不同的线程中对同一个对象运行methodA()两次。在methodB()中也是如此。

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

91551 次浏览

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

你同步的是对象而不是类。所以它们不能同时在同一物体上运行

在示例中,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
}
}
}

同步块语法允许指定一个特定的对象,执行线程需要在该对象上获得内在锁才能进入块。

重要的是要明白,即使我们把“同步”;关键词论个别方法,核心概念是内在锁的幕后。

下面是Java教程如何描述这种关系:

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

每个对象都有一个与之相关的内在锁。按照惯例,需要独占和一致访问对象字段的线程必须在访问对象字段之前获得对象的内在锁,然后在使用完对象字段时释放内在锁。线程在获得锁和释放锁之间拥有内在锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。另一个线程在试图获取锁时将阻塞。

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

Java线程在进入< em > < / em >实例同步Java方法时获得对象级锁,在进入< em >静态< / em >同步Java方法时获得类级锁

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

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

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

等于:

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

清楚地说,静态同步和非静态同步方法都可能同时或并发地运行,因为一个有对象级锁,另一个有类级锁。

来自oracle文档链接

使方法同步有两个效果:

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

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

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

看看这个文档页面来理解内在锁和锁的行为。

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

将您的代码想像如下所示:

class A {


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


public void methodB() {
synchronized(this){
// method B body
}
}
所以,在方法层面上同步仅仅意味着同步(这个)。 如果任何线程运行该类的方法,它将在开始执行之前获得锁,并持有它,直到方法执行完成
但是我可以在不同的线程上运行methodB(),而methodA()仍然是 跑步吗?(同一对象)< / p >

的确,这是不可能的!

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

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

不容易理解的带有同步的关键思想是,它只有在相同的对象实例上调用方法时才会起作用——它已经在答案和注释中突出显示了——

下面的示例程序是清楚地查明相同的-

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();
}
}
}

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

output with noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () 评论 - output is in order methodA in > methodA Out ..methodB in > methodB Out  output with *noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects()*备注 < / p >

和输出synchronizedEffectiveAsMethodsCalledOnSameObject () 评论 - 输出显示Thread1和Thread0在突出显示的部分-

同时访问methodA

 output with *synchronizedEffectiveAsMethodsCalledOnSameObject()* annotated

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

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