Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/351.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何解决这个僵局_Java_Multithreading_Concurrency_Deadlock - Fatal编程技术网

Java 如何解决这个僵局

Java 如何解决这个僵局,java,multithreading,concurrency,deadlock,Java,Multithreading,Concurrency,Deadlock,我有两条线。这两个线程必须调用send()(然后调用receive())或receive(),但是这段代码有一个很好的死锁。有没有办法解决这个问题 public class C { public static void main(String[] args) { Z z1=new Z(); Z z2=new Z(); z1.setZ(z2); z2.

我有两条线。这两个线程必须调用
send()
(然后调用
receive()
)或
receive()
,但是这段代码有一个很好的死锁。有没有办法解决这个问题

    public class C 
    {
        public static void main(String[] args) 
        {
            Z z1=new Z();
            Z z2=new Z();
            z1.setZ(z2);
            z2.setZ(z1);
            z1.start();
            z2.start();
        }
    }

    class Z extends Thread
    {
        Z z;
        Object lock=new Object();

        public void setZ(Z zz)
        {
            z=zz;
        }

        public void run()
        {
            new Thread()
            {
                public void run()
                {
                    z.send();
                }
            }.start();
            new Thread()
            {
                public void run()
                {
                    z.send();
                }
            }.start();
        }

        public void send()
        {
            synchronized(lock)
            {
                System.out.println("[Z] Send");
                z.receive();
            }
        }

        public void receive()
        {
            synchronized(lock)
            {
                System.out.println("[Z] Receive");
            }
        }
    }

这里的僵局很明显

z1
产生两个新线程;我们把它们叫做T1和T2。它还创建了一个名为
lock
的对象实例,我们称之为L1。
z2
也这样做;让我们调用线程T3和T4以及锁L2

现在,让我们假设*,T1首先开始

  • T1在Z实例z2上调用send方法
  • 此方法使T1获取锁L2,然后打印出“[Z]发送”。然后,该方法调用z1实例上的receive方法
  • z1实例上的接收方法使T1获取锁L1,然后打印出“[Z]接收”
  • T1释放锁L1,然后退出z1上的接收方法
  • T1释放锁L2,然后退出z2上的send方法
  • 现在,让我们假设在步骤2和3之间,T3启动并执行以下操作:

  • T3在Z实例z1上调用send方法
  • 此方法使T3获取锁L1,然后打印出“[Z]发送”。然后,该方法调用z2实例上的receive方法
  • z2实例上的receive方法导致T3尝试获取锁L2
  • L2已由T1持有,尚未释放。所以T3等待
  • 切换回T1,步骤3现在变为:

  • z1实例上的receive方法使T1尝试获取锁L1
  • L1已由T3持有,尚未释放。所以T1等待
  • 上下文切换到T3,L2仍由T1保持,T3等待。 上下文切换到T1,L1仍由T3保持,T1等待

    ……等等

    这就是死锁可能发生的位置的解释。要解决此问题,您可能希望将对z.receive()的调用移动到send中的同步块之外,以便在调用其他实例的received方法之前释放当前实例中的锁:

        public void send()
        {
            synchronized(lock)
            {
                System.out.println("[Z] Send");
            }
            z.receive();
        }
    
    编辑

    如果锁旨在保护所有实例不并发执行,则可以使用一个跨所有线程共享的锁,因此:

    class Z extends Thread
    {
        static final Object lock=new Object();
        ...
    }
    
    现在我们只有一个锁实例,让我们将其称为L0,再看看上面的步骤是如何运行的:

  • T1在Z实例z2上调用send方法
  • 此方法使T1获取锁L0,然后打印出“[Z]发送”。然后,该方法调用z1实例上的receive方法
  • z1实例上的receive方法使T1再次获取锁L0;因为它已经持有此锁,所以可以继续。它打印出“[Z]接收”
  • T1退出z1上的接收方法
  • T1释放锁L0,然后退出z2上的send方法
  • 同样,假设在步骤2和3之间,T3启动并执行以下操作:

  • T3在Z实例z1上调用send方法
  • 此方法导致T3尝试获取锁L0
  • L0已由T1持有,尚未释放。所以T3等待
  • T1中的这个时间步骤3不受影响。这意味着它仍然可以继续并最终释放锁L0,这将最终让T3获取该锁并继续

    没有更多的僵局

    __


    *线程启动顺序永远不能保证与调用
    start()
    方法的顺序相同,但在这种情况下,在所有可能的情况下都有死锁的风险。

    最简单的方法是使用静态类级锁,而不是非静态锁,它只需要一行更改

       private static Object lock=new Object();
    
    首先,让我们看看问题的根源: 在本例中,死锁发生是因为两个Z实例彼此持有锁(共享资源),并且都在等待对方的资源完成其任务。而且,没有人能够离开它所持有的资源上的锁

    由于锁是非静态的,z的每个实例都有自己的锁,因此在调用send()时允许两个z实例一起获取实例级锁,这会导致死锁,因为调用receive()需要对已经锁定的对象进行锁定

    这可以通过使用静态/类级别锁来解决。通过在静态锁上同步,您将同步整个类方法和属性(与实例方法和属性相反)。 换句话说,Z类的所有实例都将共享该对象上锁定的对象,因此没有两个实例能够同时锁定该对象

    一旦将锁定对象设置为静态,程序将能够按如下方式运行:

  • z1启动runnable任务时,获取类级锁并发送()
  • z2试图发送(),但z1已经持有锁,因此它等待z1释放锁
  • 由于z1持有Z类上的锁,因此它能够接收()
  • 只有当z1完成所有工作后,z2才能开始获取Z类上的锁并继续发送和接收 PS:如果您需要更多解释,请查看以下链接以了解有关差异的更多详细信息:

    死锁在哪里?z1和z2都启动。两者都在同步块内调用send()。然后它们都调用receive,但othet线程位于另一个同步块中。但是您没有向另一个线程发送任何内容,而是在与
    send()
    相同的线程上调用
    receive()
    。一个线程只需按住另一个线程,直到它完成
    receive()
    。您对代码的期望是什么?一个线程应该等待另一个线程完成吗?或者别的什么?这个练习到底想证明什么?你说的是“两个线程”,但我看到了七个线程:主线程启动两个线程,每个线程启动两个线程