2016-04-28 79 views
0

我試圖做這樣的事情:同步和線程工作不正常

有是有一個名爲n和兩個方法put()get()字段,設置n的值或檢索值類Q n。然後有兩個類ProducerConsumerProducer類有一個線程調用putconsumer類有一個線程調用get。我試圖用Object lock這是Singleton類的唯一實例LockClass.

所以要進行同步,這是我的班問:

public class Q { 

    int n; 

    public void put(int n){ 
     System.out.println("Put " + n); 
     this.n = n; 
    } 

    public int get(){ 
     System.out.println("Got " + n); 
     return n; 
    } 
} 

LockClass:

public class LockClass { 

     private static Object Lock = new Object(); 

     private LockClass(){ 

     } 


     public static Object getLock(){ 
      return Lock; 
     } 
    } 

消費者:

public class Consumer implements Runnable { 

    Thread t; 
    Q q; 


    public Consumer(Q q){ 
     this.q = q; 
     t = new Thread(this); 
     t.start(); 
    } 
    /* (non-Javadoc) 
    * @see java.lang.Runnable#run() 
    */ 
    @Override 
    public void run() { 
     // TODO Auto-generated method stub 
     while(true){ 

      synchronized(LockClass.getLock()){ 
       q.get(); 
      } 
      try { 
       System.out.println("Consumer slept"); 
       Thread.sleep(1000); 
       System.out.println("Consumer awake"); 

      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 

} 

生產者:

public class Producer implements Runnable { 

    Q q; 
    Thread t; 
    int i; 

    public Producer(Q q){ 
     this.q = q; 
     t = new Thread(this); 
     t.start(); 
     i = 0; 
    } 
    /* (non-Javadoc) 
    * @see java.lang.Runnable#run() 
    */ 
    @Override 
    public void run() { 
     // TODO Auto-generated method stub 
     while(true){ 
      i++; 
      synchronized(LockClass.getLock()){ 
       q.put(i); 
      } 
      try { 
       System.out.println("Producer slept"); 
       Thread.sleep(1000); 
       System.out.println("Producer awake"); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 


} 

DemoClass:這個類的主要功能

public class DemoClass { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     // TODO Auto-generated method stub 
     Q q = new Q(); 

     Producer prod = new Producer(q); 
     Consumer cons = new Consumer(q); 
    } 

} 

所以,當我運行這個上面的代碼中,我得到的是這樣的:

Put 1 
Producer slept 
Got 1 
Consumer slept 
Consumer awake 
Producer awake 
Got 1 
Consumer slept 
Put 2 
Producer slept 
Consumer awake 
Producer awake 
Got 2 
Consumer slept 
Put 3 
Producer slept 
Consumer awake 
Producer awake 
Got 3 
Consumer slept 
Put 4 
Producer slept 

所以,我其實做的線程在生產者和消費者中睡眠,以便有足夠的時間進行上下文切換。現在,當兩個人都需要同時睡覺的時候,如果製片人先睡,它應該首先醒來。但正如我在輸出中看到的那樣,製片人首先睡覺,但仍然是消費者首先醒來,並且無論哪個線程首先被喚醒,都應該獲得鎖定,並且另一個線程應該等待鎖定被釋放。

爲什麼它不按我期待的方式工作?我錯過了什麼嗎?

+1

每當你聽到「生產者」和「消費者」,你應該考慮的是'BlockingQueue'的第一件事。生產者將對象循環放入隊列中(在這種情況下對象可以是'Integer'對象),並且消費者循環取出對象。如果消費者嘗試刪除對象時隊列爲空,則消費者將被阻止,直到對象變爲可用。而且,如果隊列具有較高的大小限制(通常是一個好主意),那麼只要隊列變滿,生產者就會被阻塞。 –

+0

@jameslarge:非常感謝! :)將研究一個BlockingQueue –

回答

1

睡眠超時不保證是嚴格的,所以一個線程可以稍後睡覺並提前喚醒絕對有效。

[...]受精度和系統計時器和調度程序

的準確性理論上它甚至有可能爲一個線程使動作兩次,而:從了Thread.sleep Java文檔報價第二個線程將會休眠。

如果你想兩個線程充當了一個又一個,使用等待通知機制:

生產

Object lock = LockClass.getLock(); 
synchronized(lock){ 
    q.put(i); 
    lock.notifyAll(); 
    lock.wait();  
} 

消費者

Object lock = LockClass.getLock(); 
synchronized(lock){ 
    q.get(i); 
    lock.notifyAll(); 
    lock.wait();  
} 
+0

所以,我實際上是試圖使用單一的鎖對象沒有等待,通知和notifyall使用相同的功能。因此,如果兩個線程同時休眠,並且假設線程1比線程2早睡,那麼在此期間線程2將進入休眠狀態(假設它在xms之後休眠)。現在,當thread1喚醒時,JVM將控制thread1,因爲沒有其他線程在那裏。同時線程2將被喚醒,並且應該在線程1已經鎖定的時候等待。這應該繼續......在這種理解中,我錯了嗎? –

+0

@PriyanshGoel如果你不想使用睡眠,儘量讓兩個線程之間有差距,例如比第一個線程晚500ms開始第二個線程 – AdamSkywalker

+0

-1邀請新手使用wait()/ notify()/ notifyAll ()機制的方式,他們不打算被使用。等待/通知是本網站上新手的主要混淆源。它是一個底層原語,旨在用於[以非常特定的方式](https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html)來實現更高級別的同步對象。大多數問題(包括這個問題)都可以使用標準庫提供的更高級別的對象來解決。 (例如,'BlockingQueue'在這種情況下可以很好地工作。) –