2015-06-04 190 views
0

我有兩個同步的方法,我使用中介設計模式。 我試圖避免死鎖,這是(據我所知)例如,當一個線程鎖定變量res1但鎖定變量res2時。另一個線程需要res1的鎖,但對res2有鎖 - 導致死鎖,對吧?同步方法,以避免死鎖

假設我對死鎖的理解是正確的,那麼我的問題是我是否已經解決了這個代碼中的死鎖問題?

我有兩個同步的方法和兩個線程。

public class Producer extends Thread { 
    private Mediator med; 
    private int id; 
    private static int count = 1; 

    public Producer(Mediator m) { 
     med = m; 
     id = count++; 
    } 

    public void run() { 
     int num; 
     while(true) { 
      num = (int)(Math.random()*100); 
      med.storeMessage(num); 
      System.out.println("P-" + id + ": " + num); 
     } 
    } 
} 

public class Consumer extends Thread { 
    private Mediator med; 
    private int id; 
    private static int count = 1; 

    // laver kopling over til mediator 
    public Consumer(Mediator m) { 
     med = m; 
     id = count++; 
    } 

    public void run() { 
     int num; 
     while(true) { 
      num = med.retrieveMessage(); 
      System.out.println("C" + id + ": " + num); 
     } 
    } 
} 

public class Mediator { 
    private int number; 
    private boolean slotFull = false; 

    public synchronized void storeMessage(int num) { 
     while(slotFull == true) { 
      try { 
       wait(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
     slotFull = true; 
     number = num; 
     notifyAll(); 
    } 

    public synchronized int retrieveMessage() { 
     while(slotFull == false) { 
      try { 
       wait(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
     slotFull = false; 
     notifyAll(); 
     return number; 
    } 
} 

public class MediatorTest { 
    public static void main(String[] args) { 
     Mediator mb = new Mediator(); 
     new Producer(mb).start(); 
     new Producer(mb).start(); 
     new Producer(mb).start(); 

     new Consumer(mb).start(); 
     new Consumer(mb).start(); 
    } 
} 

回答

3

例如,當一個線程對一個變量RES1一個鎖,但需要在可變RES2鎖

重要的不是有兩個變量,重要的是必須有兩個(或更多)

名稱「res1」和「res2」意味着建議兩個資源,每個資源可能有一個或多個變量,並且每個變量都有自己的鎖。在這裏你惹上麻煩:

final Object lock1 = new Object(); 
final Object lock2 = new Object(); 

public void method1() { 
    synchronized (lock1) { 
     // Call Thread.sleep(1000) here to simulate the thread losing its time slice. 
     synchronized(lock2) { 
      doSomethingThatRequiresBothLocks 
     } 
    } 
} 

public void method2() { 
    synchronized (lock2) { 
     // Do the same here 'cause you can't know which thread will get to run first. 
     synchronized(lock1) { 
      doSomethingElseThatRequiresBothLocks() 
     } 
    } 
} 

如果線程A調用method1(),有一個很小的機會,它可能會失去其時間片(即轉運行),它成功地鎖定lock1剛過,但在鎖之前lock2

然後,當線程A等待輪到它再次運行時,線程B調用method2()。線程B將能夠鎖定lock2,但之後它會卡住,因爲lock1被線程A鎖定。此外,線程A再次運行時,它會在嘗試鎖定由線程B擁有的lock2時立即被阻止。從這一點來看,兩條線都不能夠繼續。


在實際的代碼中,它從來沒有那麼明顯。當它發生在現實生活中時,通常是因爲來自兩個或更多不同模塊的代碼之間的一些不可預知的交互,這些代碼甚至可能不知道彼此,但是訪問相同的公共資源。

1

你的基本死鎖問題的理解是正確的。關於解決死鎖問題的有效性的第二個問題,您只有1個鎖,所以默認情況下我會說「是」,因爲您描述的死鎖在這種情況下是不可能的

+0

謝謝:) 我過幾天有一個考試,我打算在描述如何解決僵局。它需要很好的設計,但我認爲它看起來像上面提到的代碼。 你知道如何解決死鎖嗎?我在想也許是一個理論上的解釋? – Charles

0

我同意@ControlAltDel的說法。你對僵局的理解與我的相符。鑑於死鎖可以通過幾種不同的方式體現出來,您描述的方式 - 通過涉及的線程(方法)不一致地獲取多個監視器會導致死鎖。

另一種方法是(例如)在按住鎖的同時睡眠。當你編碼正確時,當生產者發現slotFull = true,它等待,放棄鎖,所以另一個線程(消費者,它與生產者共享同一個Mediator實例)可能會導致此線程也可能進行在獲得通知後取得進展。如果您選擇調用Thread.sleep()(天真地希望當條件爲假時某人會導致睡眠結束),那麼它會導致死鎖,因爲此線程正在睡眠,仍然保持鎖定,拒絕訪問其他線程。

-1

每個對象都有一個鎖,當您使用synchronized關鍵字時,它會限制多個線程訪問相同的代碼塊或方法塊。

即將到來的問題,它不會造成死鎖。

如果在由多個線程共享的類中有兩個獨立屬性,則必須將訪問權限同步到每個變量,但如果一個線程正在訪問某個屬性並且另一個線程正在訪問另一個線程,則沒有問題時間。

class Cinema { 
private long vacanciesCinema1; private long vacanciesCinema2; 
private final Object controlCinema1, controlCinema2; 
public Cinema() { 
    controlCinema1 = new Object(); 
    controlCinema2 = new Object(); 
    vacanciesCinema1 = 20; 
    vacanciesCinema2 = 20; 
} 
public boolean sellTickets1(int number) { 
    synchronized (controlCinema1) { 
     if (number < vacanciesCinema1) { 
      vacanciesCinema1 -= number; 
      return true; 
     } else { 
      return false; 
     } 
    } 
} 
public boolean sellTickets2(int number) { 
    synchronized (controlCinema2) { 
     if (number < vacanciesCinema2) { 
      vacanciesCinema2 -= number; 
      return true; 
     } else { 
      return false; 
     } 
    } 
} 
public boolean returnTickets1(int number) { 
    synchronized (controlCinema1) { 
     vacanciesCinema1 += number; 
     return true; 
    } 
} 
public boolean returnTickets2(int number) { 
    synchronized (controlCinema2) { 
     vacanciesCinema2 += number; 
     return true; 
    } 
} 
public long getVacanciesCinema1() { 
    return vacanciesCinema1; 
} 

public long getVacanciesCinema2() { 
    return vacanciesCinema2; 
} 

}

class TicketOffice1 implements Runnable { 
private final Cinema cinema; 

public TicketOffice1(Cinema cinema) { 
    this.cinema = cinema; 
} 
@Override 
public void run() { 
    cinema.sellTickets1(3); 
    cinema.sellTickets1(2); 
    cinema.sellTickets2(2); 
    cinema.returnTickets1(3); 
    cinema.sellTickets1(5); 
    cinema.sellTickets2(2); 
    cinema.sellTickets2(2); 
    cinema.sellTickets2(2); 
} 

}

public class CinemaMain { 
public static void main(String[] args) { 
    Cinema cinema = new Cinema(); 
    TicketOffice1 ticketOffice1 = new TicketOffice1(cinema); 
    Thread thread1 = new Thread(ticketOffice1, "TicketOffice1"); 
    TicketOffice2 ticketOffice2 = new TicketOffice2(cinema); 
    Thread thread2 = new Thread(ticketOffice2, "TicketOffice2"); 
    thread1.start(); 
    thread2.start(); 
    try { 
     thread1.join(); 
     thread2.join(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
    System.out.printf("Room 1 Vacancies: %d\n", cinema.getVacanciesCinema1()); 
    System.out.printf("Room 2 Vacancies: %d\n", cinema.getVacanciesCinema2()); 
} 

}

+0

同步不會防止死鎖。相反,在沒有采取適當的預防措施的情況下進行同步可能會導致死鎖的可能性。 – JimN

+0

是的,應該使用同步關鍵字以防備。 – deepak