【问题标题】:Breaking object deadlock打破对象死锁
【发布时间】:2020-10-01 21:36:52
【问题描述】:

晚上好!亲爱的,我提前为糟糕的文字道歉。英语不是我的母语。问题是这样的:有代码,我什至想出了死锁是如何实现的。但是如何摆脱它是个问题。 有人告诉我要么同步捕获,要么按顺序调用线程。

我尝试如下:

  • 我从方法中删除了监视器,并在类中创建了一个静态对象并将其扔到监视器中,如果你调用线程,它会工作,但如果你将线程调用到循环中,一切都会崩溃。
  • 我还尝试将函数 bower.bowBack (this) 重写为 this.bowBack (bower),以便不会从 1 个线程调用 2 个对象,但仍然值得将其放入循环中,我们再次不这样做'没有我们需要的东西。

请告诉我如何解决这个问题?

更新:我很抱歉没有提供信息。我会改进的。 根据作业,需要输出2个流:

  • A:G 打了我
  • 答:我还击
  • G:他开枪打死我

(流 1)

  • G:他开枪打死我
  • G:我还击
  • A:G 打了我。

(流 2)

他就是这么做的。但是,一旦您将其放入循环中,它就会冻结。如何组织线程的顺序工作?我不明白如何正确实现它。

package pr1;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//package ru.philosophyit;



public class DeadLock {

  static class Friend {
    private final String name;
    
    private final Lock lock = new ReentrantLock();
    
    public Friend(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }
    
    public void bow(Friend bower) {
        
        boolean Lock_my = false;
        boolean Lock_bower = false;
        
        try
              {
                
                 while(!Lock_my || !Lock_bower)
                 {
                    Lock_my = this.lock.tryLock(100, TimeUnit.MILLISECONDS);
                    Lock_bower = bower.lock.tryLock(100, TimeUnit.MILLISECONDS);

                 }
    
                 System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
                 System.out.format("%s: стреляю в ответ!\n", this.name);
                         
                 bower.bowBack(this);
    
                 this.lock.unlock();
                 bower.lock.unlock();
                }
        catch(InterruptedException e)
        {
            e.printStackTrace();
            }
        }
             
    public void bowBack(Friend bower) {
     
        System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
    }
  }
  

  /**
   * Точка входа в программу
   
*
   * @param args аргументы командной строки
   */
  
  public static void main(String[] args) {
    Friend alphonse = new Friend("Alphonse");
    Friend gaston = new Friend("Gaston");
    
    for (int i = 0; i < 5; i++)
    {
    new Thread(() -> alphonse.bow(gaston)).start();
           
    new Thread(() -> gaston.bow(alphonse)).start();
    }
  }
    
}

我还对静态对象进行了同步,并且循环正常工作,但输出不是连续的。即可以有 1 个流 1 个流 2 个流 2 个流 1 个流,而不是 1 个流然后 2 个流。我不知道为什么。

package pr1;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//package ru.philosophyit;



public class DeadLock {

  static Object o = new Object();
    
  static class Friend {
    private final String name;
    
    
    public Friend(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }
    
    public void bow(Friend bower) {
        
        //synchronized(getClass())
        synchronized(o)
        {   
                 System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
                 System.out.format("%s: стреляю в ответ!\n", this.name);
                         
                 bower.bowBack(this);
    }
    }
             
    public void bowBack(Friend bower) 
    {
        //synchronized(getClass())
        synchronized(o)
        {
        System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
    }
    }
  }
  
 
  public static void main(String[] args) {
    Friend alphonse = new Friend("Alphonse");
    Friend gaston = new Friend("Gaston");
    
  // for (int i = 0; i < 10; i++)
    {
    new Thread(() -> alphonse.bow(gaston)).start();
           
    new Thread(() -> gaston.bow(alphonse)).start();
    }
  }
    
}

事实证明2,代码非常合适,线程工作的顺序无关紧要,但消息的顺序很重要。但是方法 2 变得复杂,因为当线程增加时,我们需要大量的同步对象。他们说你只需要改变几行,一切都会奏效。 3代码是原始形式的任务本身的代码(因此您需要更改行)。 我知道 2 个线程使用不同的对象,因此同步不能正常工作(我们在 bowBack 上阻塞,也许值得重写它?)

package pr1;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//package ru.philosophyit;

public class DeadLock {

  static Object o = new Object();
  
  static class Friend {
    private final String name;
    
    
    public Friend(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }
    
    public synchronized void bow(Friend bower) {
        
        {   
                 System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
                 System.out.format("%s: стреляю в ответ!\n", this.name);
                         
                 bower.bowBack(this);
    }
    }
             
    public synchronized void bowBack(Friend bower) 
    {
            {
        System.out.format("%s: %s подстрелил меня!\n", this.name, bower.getName());
    }
    }
  }
  
 
  public static void main(String[] args) {
    Friend alphonse = new Friend("Alphonse");
    Friend gaston = new Friend("Gaston");
    
   for (int i = 0; i < 10; i++)
    {
    new Thread(() -> alphonse.bow(gaston)).start();
           
    new Thread(() -> gaston.bow(alphonse)).start();
   
    
    }
  }
    
}

【问题讨论】:

  • 语言障碍是一回事,但如果您可以发布 MCVE 并指出您遇到的确切错误/情况,那就太好了。 “一切都崩溃了”和“我们没有我们需要的东西”并不是那么有帮助。
  • 为了避免死锁,您需要在所有线程中以相同的顺序锁定对象,例如首先锁定具有较小哈希码的对象。或者您可以使用Lock 接口和方法tryLock 的实现与超时
  • 欢迎来到 StackOverflow! “如果你调用线程,它会起作用,但如果你把线程调用成一个循环,一切都会崩溃”。您能否编辑您的问题以包含将线程调用到循环中的代码?这将帮助我们区分有效/无效的情况。
  • @Ivan 我尝试使用超时的 tryLock,但问题仍然存在于循环中。如果我运行它,一切都会进入睡眠状态。显然我不太明白如何正确实现这一点
  • 你为什么要启动 10 个线程?关于谁在空闲时再次抓住锁,您实际上没有任何流量控制。还要注意你的两个锁循环,很可能一个线程会抓住一个锁,然后另一个线程会抓住另一个锁,然后他们永远不会脱离那个循环。这与锁定顺序有关。

标签: java multithreading oop object deadlock


【解决方案1】:

我们可以将箭头视为一种资源吗?

import java.util.concurrent.*;


public class Bowmen implements Runnable{
    String name;
    Bowmen target;
    SynchronousQueue<Object> queue = new SynchronousQueue<>();
    public Bowmen(String name){
        this.name = name;
    }
    
    public void shoot(String name, Object o) throws InterruptedException{
        System.out.println(this.name + " : " + name + " shot me.");
        queue.put(o);
    }
    
    public void run(){
        try{
            Object o = queue.take();
            System.out.println(this.name + " : I will shoot " + target.name);
            target.shoot(name, o);
        } catch(InterruptedException e){
            //just finish.
        }
    }
    public void setTarget(Bowmen t){
        target = t;
    }
    
    public static void main(String[] args) throws Exception{
        
        Bowmen a = new Bowmen("a");
        Bowmen b = new Bowmen("b");
        
        a.setTarget(b);
        b.setTarget(a);
        
        for(int i = 0; i<5; i++){
            new Thread(a).start();
            new Thread(b).start();
        }
        //all the threads are waiting because there is no resource to share yet.
        a.shoot( null, new Object() );
    }

}

这将产生输出

a : null 向我开枪。
a : 我会拍 b
b : 开枪了。
b : 我会拍一个
...

它会以“a : b shot me”结尾。但是程序不会完成,因为没有线程从队列中取出来让最后一次拍摄完成。

【讨论】:

    猜你喜欢
    • 2012-02-21
    • 2020-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多