繁体   English   中英

为什么这个程序中的同一个对象没有死锁 - Java 多线程

[英]Why no deadlock on the same object in this program - Java Multithreading

我有以下三节课。 Main,两个线程和方法同步的Obj

public class DeadLocks {

    public static void main(String[] args) {
        SyncObj so = new SyncObj();
        ThreadObj to = new ThreadObj(so);
        ThreadObj1 to1 = new ThreadObj1(so);

        to.start();
        to1.start();
    }
}

class SyncObj {
    synchronized void foo() {
        System.out.println("Foo Started");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bar();
    }

    synchronized void bar() {
        System.out.println("bar started");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        foo();
    }

}

class ThreadObj1 extends Thread {
    SyncObj so;

    public ThreadObj1(SyncObj so) {
        this.so = so;
    }

    @Override
    public void run() {
        so.bar();
    }
}

class ThreadObj extends Thread {
    SyncObj so;

    public ThreadObj(SyncObj so) {
        this.so = so;
    }

    @Override
    public void run() {
        so.foo();
    }
}

在上面的代码中,我在同一个对象上调用了同步方法。 两个方法同时执行和调用,不存在死锁情况。 谁能解释一下为什么? 抱歉问了这么愚蠢的问题。

据我所知,您在这两种情况下都使用相同的对象( so )。 所以没有死锁的情况。 您需要锁定两个或多个对象,其中每个临界区都需要与它持有的锁不同的锁。 另一个锁由“另一个”线程持有。

令人困惑,这将启发:“ https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html

在您描述的场景中,您never ever deadlock 在这种情况下,只有一个so正被共享,并且正在与同步。

我举个例子来说明一下:

假设安迪和桑迪正在玩两个足球B1B2

现在进一步假设 Andy 和 Sandy 分别拥有球B1B2 例如,安迪有球B1 ,桑迪有球B2

现在他们设计了一个游戏,其中每个人都需要两个球。 现在 Sandy 也想要B1球,同时,Andy 想要B2 球

并且他们两个都不能放弃他们持有的球。 安迪不会放弃B1 ,反之亦然。

So both of them cannot continue and are stuck. We call this a deadlocked situation.

希望这个例子有帮助。 您可以发挥您的想象力将比赛中的球数增加到 3 或 4 个(依此类推……)和/或增加球员人数。

您不会遇到死锁,因为您的程序不满足形成它的四个必要条件中的两个:

  • 互斥- 是的,
  • 保持部分分配- 不,
  • 没有先发制人- 是的,
  • 循环依赖- 没有

您至少需要两个资源“A”和“B”才能形成死锁。 一个线程应该抓取“A”并尝试抓取“B”,而另一个线程应该抓取“B”并尝试抓取“A”。

我不知道你为什么期望这里会出现僵局。 确实只有一个对象可以访问同步部分,但是它可以随心所欲地访问。 您可以修改代码以使其清楚:

public class DeadLocks {

     public static void main(String[] args) {
         SyncObj so = new SyncObj();
         ThreadObj to = new ThreadObj(so);
         ThreadObj1 to1 = new ThreadObj1(so);

         to.start();
         to1.start();
     }
 }

 class SyncObj {
     synchronized void foo(String msg) {
         System.out.println("Foo Started: " + msg);
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         bar(msg);
     }

     synchronized void bar(String msg) {
         System.out.println("bar started: " + msg);
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         foo(msg);
     }

 }

 class ThreadObj1 extends Thread {
     SyncObj so;

     public ThreadObj1(SyncObj so) {
         this.so = so;
     }

     @Override
     public void run() {
         so.bar("TO1");
     }
 }

 class ThreadObj extends Thread {
     SyncObj so;

     public ThreadObj(SyncObj so) {
         this.so = so;
     }

     @Override
     public void run() {
         so.foo("TO");
     }
 }

您可以看到以下输出:

Foo Started: TO
bar started: TO
Foo Started: TO
bar started: TO
Foo Started: TO
bar started: TO

您可以将 'synchronized' 视为 lock(this),其中 'this' 是一个 SyncObj 实例。 因此只有一个锁,不可能获得死锁。

虽然其他人已经指出只有当你有两个资源时才会发生死锁,其中每个线程锁定一个然后等待另一个,但我认为缺少一个关键点,你的整个困惑可能来自:

方法上的synchronize不会为该特定方法创建锁,而是为其所属的整个对象创建锁。 因此,您的课程相当于:

class SyncObj {
    void foo() {
        synchronized(this) {
            System.out.println("Foo Started");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bar();
        }
    }

    void bar() {
        synchronized(this) {
          System.out.println("bar started");
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          foo();
        }
    }
}

现在应该更清楚了,为什么你没有陷入僵局。

您可以通过引入两种资源(每个方法一个)来轻松修改代码,使其容易出现死锁:

class SyncObj {
    private Object foolock = new Object();
    private Object barlock = new Object();

    void foo() {
        synchronized(foolock) {
            System.out.println("Foo Started");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bar();
        }
    }

    void bar() {
        synchronized(barlock) {
          System.out.println("bar started");
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          foo();
        }
    }
}

使用单个实例不可能发生死锁,因为同一个对象的两个线程不能访问多个同步方法。 在上面的例子中,如果线程 1 正在访问 foo 方法。 线程 2 不能访问 foo 或 bar 方法。 直到线程 1 完成它的任务

暂无
暂无

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

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