简体   繁体   English

理解Java并发中的外来方法

[英]Understanding alien methods in Java concurrency

I'm reading J. Bloch's effective Java and now I'm at the section about alien methods. 我正在阅读J. Bloch的有效Java,现在我正在关于外来方法的部分。

I'm trying to understand alien methods in Java concurrency and the harm they could do. 我正在尝试理解Java并发中的外来方法及其可能造成的危害。 As he said, we basically don't know what alien methods could do and we may end up deadlocking. 正如他所说,我们基本上不知道外星人的方法可以做什么,我们可能最终陷入僵局。 I tried to reproduce such a deadlocking behavior writing the following simple app (The alien method is in the same class for simplicity): 我试图重现这样一个死锁行为编写以下简单的应用程序(为简单起见,外来方法在同一个类中):

public class App {
    private static StringBuffer lines = new StringBuffer();

    public static void modifyLines(){
        System.out.println("Invocation modifyLines() started by " + Thread.currentThread().getName());
        synchronized (lines) {
            System.out.println("Entering modifyLines() synchronized " + Thread.currentThread().getName());
            lines.append("Modified");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        synchronized (lines) {
            System.out.println("Entering main() synchronized by " + Thread.currentThread().getName());
            alienMethod();
        }
    }

    public static void alienMethod(){
        ExecutorService es = Executors.newSingleThreadExecutor();
        es.submit(new Runnable() {
            @Override
            public void run() {
                modifyLines();
            }
        });
        es.shutdown();
    }
}

I expected a deadlock to take place and that the thread spawned by calling to alienMethod() would never enter the synchronized block within modifyLines() . 我期望的僵局发生,并且通过调用来催生线程alienMethod()绝不会内进入synchronized块modifyLines() But the program prints the following: 但该程序打印以下内容:

Entering main() synchronized by main
Invocation modifyLines() started by pool-1-thread-1
Entering modifyLines() synchronized pool-1-thread-1

Which means the deadlock did not happen. 这意味着没有发生僵局。 Why? 为什么? What's wrong with the alien method example? 外星人方法示例有什么问题?

This is a very old question and answer is also accepted but I had to took time to dig this old grave because I think answer is not entirely correct and could mislead. 这是一个非常古老的问题和答案也被接受,但我不得不花时间挖掘这个古老的坟墓因为我认为答案不完全正确并且可能误导。

Let me first start by highlighting how accepted answer is broken - if you run with es.awaitTermination(2, TimeUnit.SECONDS); 首先让我首先强调一下接受的答案如何被打破 - 如果你运行es.awaitTermination(2, TimeUnit.SECONDS); then you will not get deadlock, because there was never a deadlock situation in the given code. 那么你就不会遇到死锁,因为在给定的代码中从来没有死锁的情况。 Deadlock occurs when you have 2 threads waiting for each other to release a lock, typically for a deadlock to occur you would have at least 2 locks and 2 threads. 当你有2个线程等待彼此释放锁时会发生死锁,通常会发生死锁,你至少会有2个锁和2个线程。 As per the suggested answer, what was happening was that main thread was held using awaitTermination and since the main thread is holding the lock so the new thread had to "wait" until main thread releases the lock, now with Long.MAX_VALUE this wait period was too huge so it looked like a deadlock, but actually it was "waiting" instead of deadlock , and there is a difference in deadlock and waiting, in order to resolve a deadlock you have to adjust your locking code. 根据建议的答案,发生的事情是主线程是使用awaitTermination ,因为主线程持有锁,所以新线程必须“等待”,直到主线程释放锁,现在使用Long.MAX_VALUE这个等待期太大了所以它看起来像一个死锁, 但实际上它是“等待”而不是死锁 ,并且死锁和等待存在差异,为了解决死锁,你必须调整你的锁定代码。

Now coming to alien methods: basically for a class a method would be considered as a " alien method " if it doesn't have any information about it or in other words implementation details of the method, now typically each class would not have any information about other class implementations (and this is what is expected also - " loose coupling ") so "as such" every method of class B is "alien" for class A but we don't consider it alien in general context because that's what is expected, we call methods as alien methods only in context of synchronization , so when there is a synchronization and from the synchronized block a method is called for which the object doesn't have any information and object cannot be sure if it could result in deadlock then that method is called as "alient method". 现在来到外来方法:基本上对于一个类,如果一个方法没有关于它的任何信息,或者换句话说该方法的实现细节,则该方法将被视为“ 外来方法 ”,现在通常每个类都没有任何信息关于其他类的实现(这也是预期的 - “ 松散耦合 ”)所以“就这样”B类的每个方法对于A类来说都是“外来的”但我们不认为它在一般情况下是外来的,因为那是什么我们只在同步的上下文中调用方法作为外来方法 ,所以当有同步时,从synchronized块调用一个方法,对象没有任何信息,对象不能确定它是否会导致死锁然后该方法称为“方法”。

Now, below is an example code to demonstrate (*read code comments marked with "*****"*) alien method, it shows that how depending on what the client method is doing, a deadlock may occur or a deadlock may not occur; 现在,下面是一个示例代码来演示(*读取标记为“*****”的代码注释*)外来方法,它显示了如何根据客户端方法的操作,可能发生死锁或死锁发生; here you have deadlock and not waiting , I have not used any waiting code. 在这里你有死锁而不是等待 ,我没有使用任何等待代码。

SetObserver.java: SetObserver.java:

public interface SetObserver {
    void added(MyClass mc, SetObserver sc);
}

MyClass.java: MyClass.java:

import java.util.concurrent.*;

public class MyClass {

    static Object o1 = new Object();

    public void test1(){
        synchronized(o1){
            System.out.println("test1");
        }
    }

    public void test3(SetObserver sc) throws InterruptedException{
        synchronized(o1){
            for (int i = 0; i < 100; i++) {
                System.out.print("test3 >>" + i);
                sc.added(this, sc);
                synchronized(sc){
                    System.out.println("<<");
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyClass mc = new MyClass();
        mc.test3(new SetObserver() {

            @Override
            public void added(final MyClass mc, final SetObserver sc) {
                // ***** This will not cause deadlock because it doesn't spawn a new thread, even though it synchronize on same object. *****
                /*synchronized(sc){
                    mc.test1();
                }*/

                // ***** This causes a deadlock because it spawns a new thread, so it will cause lock contention among threads. *****
                ExecutorService xc = Executors.newFixedThreadPool(1);
                xc.execute(new Runnable() {

                    @Override
                    public void run() {
                        synchronized(sc){
                            System.out.println("Calling test1");
                            mc.test1();
                        }
                    }
                });
                xc.shutdown();
            }
        });
    }
}

Your alienMethod() does not wait till the submitted task is finished. 您的alienMethod()不会等到提交的任务完成。 If you wait for it, you will have a deadlock: 如果你等待它,你将陷入僵局:

public static void alienMethod() throws InterruptedException{
    ExecutorService es = Executors.newSingleThreadExecutor();
    es.submit(new Runnable() {
        @Override
        public void run() {
            modifyLines();
        }
    });
    es.shutdown();
    es.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}

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

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