简体   繁体   English

两个线程可以同时访问同步方法吗?

[英]Can two threads access a synchronized method at the same time?

    public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

when i run this program i am getting the output as 当我运行该程序时,我得到的输出为

Alphonse: Gaston has bowed to me! 阿方斯:加斯顿向我鞠躬! Gaston: Alphonse has bowed to me! 加斯顿:阿方斯向我鞠躬!

So can two threads access a synchronized method at the same time? 那么,两个线程可以同时访问同步方法吗?

Can two threads access a synchronized method at the same time? 两个线程可以同时访问同步方法吗?

It depends on what object instance the two threads are trying to lock on. 这取决于两个线程试图锁定的对象实例。 Two threads cannot access the same synchronized method on the same object instance. 两个线程无法访问同一对象实例上的相同synchronized方法。 One will get the lock and the other will block until the first thread leaves the method. 一个将获得锁定,另一个将阻塞,直到第一个线程离开该方法。

In your example, instance methods are synchronized on the object that contains them. 在您的示例中,实例方法在包含它们的对象上同步。 In this case, when you call alphonse.bow(...) you are locking on the alphonse object. 在这种情况下,当您调用alphonse.bow(...)您将锁定alphonse对象。 gaston.bow(...) locks gaston . gaston.bow(...)锁定gaston

There are a couple of ways that you can get multiple instances of an object to lock on the same object. 您可以通过两种方法来获取一个对象的多个实例以锁定同一对象。

  • You could make the method be static and synchronized in which case they would lock on the class object itself. 您可以将方法设为staticsynchronized在这种情况下,它们将锁定类对象本身。 There is only one of these objects per class loader. 每个类加载器仅这些对象之一。

     public static synchronized void bow(Friend bower) { 
  • They could both lock on a defined static object. 它们都可以锁定已定义的静态对象。 Something like: 就像是:

     private static final Object lockObject = new Object(); ... public void bow(Friend bower) { synchronized (lockObject) { .... } } 
  • Or you could could pass in the object to lock on if you didn't want to make it static. 或者,如果您不想将其设为静态,则可以传入该对象以进行锁定。

Your output could be something like the following: 您的输出可能类似于以下内容:

  1. the gaston thread (may) start first and calls bow(alphonse) gaston线程(可能)首先启动并调用bow(alphonse)
  2. this locks the gaston object and outputs: Gaston: Alphonse has bowed to me! 这将锁定gaston物体并输出: Gaston: Alphonse has bowed to me!
  3. it calls alphonse.bowBack(this) . 它调用alphonse.bowBack(this)
  4. this call locks the alphonse object and outputs: Alphonse: Gaston has bowed back to me! 此调用将锁定alphonse对象并输出: Alphonse: Gaston has bowed back to me!
  5. alphonse.bowBack(this) exits, unlocking the alphonse object. alphonse.bowBack(this)退出,解锁alphonse对象。
  6. gaston.bow(alphonse) exits, unlocking the gaston object. gaston.bow(alphonse)退出,解锁gaston对象。
  7. then the gaston thread exits. 然后gaston螺纹退出。
  8. the alphonse thread (may) start next and calls bow(gaston) alphonse线程(可能)接下来开始并调用bow(gaston)
  9. this locks the alphonse object and outputs: Alphonse: Gaston has bowed to me! 这将锁定alphonse对象并输出: Alphonse: Gaston has bowed to me!
  10. it calls gaston.bowBack(this) . 它调用gaston.bowBack(this)
  11. this call locks the gaston object and outputs: Gaston: Alphonse has bowed back to me! 此调用将锁定gaston对象并输出: Gaston: Alphonse has bowed back to me!
  12. gaston.bowBack(this) exits, unlocking the gaston object. gaston.bowBack(this)退出,解锁gaston对象。
  13. alphonse.bow(gaston) exits, unlocking the alphonse object. alphonse.bow(gaston)退出,解锁alphonse对象。

This could happen in a number of different orders. 这可能以许多不同的顺序发生。 The alphonse thread could run first even though it's start() method gets called at a later time. 即使稍后调用start()方法, alphonse线程也可以首先运行。 The only thing that the locks save you from is the calling of alphonse.bow(...) if alphonse.bowBack(...) is currently running. 如果alphonse.bowBack(...)当前正在运行,那么锁alphonse.bow(...)您免于遭受的唯一alphonse.bow(...)是调用alphonse.bow(...) As @user988052 pointed out, because each thread locks their own object and then tries to lock the other, you can easily get a deadlock. 正如@ user988052指出的那样,由于每个线程都锁定自己的对象,然后尝试锁定另一个对象,因此很容易会出现死锁。

So can two threads access a synchronized method at the same time? 那么,两个线程可以同时访问同步方法吗?

Yes and no: 是和否:

  • Yes, if the method is called on different instances of the class. 是的,如果在类的不同实例上调用了该方法。

  • No, two threads can't simultaneously call synchronized methods on the same instance of the class. 不,两个线程不能同时在类的同一实例上调用同步方法。 This is the case even if the two threads call different methods (as long as the instance is the same). 即使两个线程调用不同的方法也是如此(只要实例相同)。

I didn't check your code in detail but I think I recognize the typical example as to how to create a deadlock. 我没有详细检查您的代码,但我想我知道有关如何创建死锁典型示例。

However you shouldn't call it just once to try to create the deadlock. 但是,您不应仅一次调用它来尝试创建死锁。

Create threads in a loop and there's a very high probability you'll get your deadlock: 循环创建线程,极有可能导致死锁:

for ( int i = 0; i < 1000; i++ ) {
    final Friend alphonse =
        new Friend("Alphonse");
    final Friend gaston =
        new Friend("Gaston");
    new Thread(new Runnable() {
        public void run() { alphonse.bow(gaston); }
    }).start();
    new Thread(new Runnable() {
        public void run() { gaston.bow(alphonse); }
    }).start();
}

Note that you won't deadlock your 2000 threads: only some of them will be deadlocked. 请注意,您不会死锁2000个线程:只有其中一些会死锁。 You can verify this by taking a threadump of your program/JVM. 您可以通过对程序/ JVM进行线程验证来验证这一点。

With the synchronized keyword you take a lock on an instance for an instance method or on a class for a static method. 使用synced关键字,您可以锁定实例方法的实例或静态方法的类。 So here you guarantee that at most one thread executes bow or bowBack on a given instance at a given time (if one thread executes bow no other thread can execute bowBack because both methods synchronize on the same lock)... 因此,在这里您可以保证最多有一个线程在给定的时间在给定的实例上执行bow或bowBack(如果一个线程在执行bow,则其他线程都不能执行bowBack,因为这两个方法都在同一锁上同步)...

One more comment: as locks are reentrant once a thread has acquired a lock it can enter other methods synchronizing on the same lock. 还有一个评论:由于锁是可重入的,一旦线程获得了锁,它就可以输入在同一锁上同步的其他方法。

As described in the Deadlock Tutorials which is where this code comes from this code will usually block. 如《 死锁教程》中 所述,此代码通常来自此代码。

When Deadlock runs, it's extremely likely that both threads will block when they attempt to invoke bowBack. 死锁运行时,两个线程极有可能在尝试调用bowBack时阻塞。 Neither block will ever end, because each thread is waiting for the other to exit bow. 两个块都不会结束,因为每个线程都在等待另一个退出弓。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM