2013-10-16 30 views
1

我正在實現一個簡單的多線程示例,其中線程1在每1000ms後打印PING,線程2在每2000ms後打印PONG。如何在java中設置線程的時間?

public class T extends Thread 
{ 
public void run() 
{ 
int i =10; 
while(i>0) 
{ 
    if(Thread.currentThread().getName().equals("T1")) 
    { 
     System.out.println("\nPING"); 
     try {Thread.sleep(1000);} 
     catch (InterruptedException e) {e.printStackTrace();} 
    } 
    else if (Thread.currentThread().getName().equals("T2")) 
    { 
     System.out.println("\nPONG"); 
     try {Thread.sleep(2000);} 
     catch (InterruptedException e) {e.printStackTrace();} 
    } 
i--; 
} 
} 

public static void main(String[] args) 
{ 
    Thread t1 = new T(); 
    Thread t2 = new T(); 
    t1.setName("T1"); 
    t2.setName("T2"); 
    t1.start(); 
    t2.start(); 
} 
} 

但是,輸出類似::

PONG

PING

PING

PING

PONG

PING

PING

PONG

PING

PING

PONG

PING

PING

PONG

PING

PONG

PONG

PONG

PONG

PONG

但是我預期的輸出應該像

PING 
PING 
PONG 
PING 
PING 
PONG 
PING 
PING 
PONG..... 

應該在代碼中做些什麼改變?

+0

你PING和PONG 10次,但PING更頻繁2次。所以它應該以一些PONGs結尾 –

+0

當Ping和Pong同時運行時,首先是完全隨機的。兩者都從0ms開始,每兩秒「見面」。請解釋行爲的預期模型,然後我們可以提出一個實現。 – Daniel

+0

我喜歡這些任務,在那裏有人試圖完成對真實世界應用程序完全無用的事情。或者他/她試圖以非常錯誤的方式達到理想目的和某種目的有用的行爲。除了可能學會你應該正確地同步一切,否則讓線程執行流程以自然的方式運行,而不是試圖成爲線程控制怪胎之外,究竟什麼是解決這個任務的成就呢?我的建議:忘記sleep()進行同步。目的,並學習如何做適當的同步。你正在窒息你的計劃而死。 – JensG

回答

2

Thread.sleep文檔:

使當前執行的線程休眠(暫時停止 執行)爲指定的毫秒數,受 精度和系統計時器和調度程序的準確性。

如果你想確保線程打印PING運行兩次,然後等待另一個線程來打印PONG(這反過來等待,然後再繼續2個ping),您需要使線程彼此同步。請參閱Object.waitObject.notify

編輯:這裏是與同步的版本(這是混亂的,但這樣是問題的原; P):

public class T extends Thread 
{ 
    //Static object to synchronize on... bad practice but this is just an example anyway 
    static Object synchOnMe = new Object(); 

    public void run() 
    { 
     int i = 10; 
     while(i > 0) 
     { 
      if(Thread.currentThread().getName().equals("T1")) 
      {    
       System.out.println("PING"); 
       try 
       { 
        Thread.sleep(1000); 

        //Synchronize on every second time (ie. wait for PONG) 
        if((i + 1) % 2 == 0) 
        { 
         synchronized(synchOnMe) 
         { 
          synchOnMe.notify(); 
          synchOnMe.wait(); 
         } 
        }     
       } 
       catch(InterruptedException e) 
       { 
        e.printStackTrace(); 
       } 

      } 
      else if(Thread.currentThread().getName().equals("T2")) 
      { 
       try 
       { 
        //Synchronize every time (ie. wait for PING) 
        synchronized(synchOnMe) 
        { 
         synchOnMe.wait(); 
         System.out.println("PONG"); 
         synchOnMe.notify(); 
        } 

        Thread.sleep(2000); 
       } 
       catch(InterruptedException e) 
       { 
        e.printStackTrace(); 
       } 


      } 
      i--; 
     } 

     //One last notify to release the other thread waiting 
     synchronized(synchOnMe) 
     { 
      synchOnMe.notify(); 
     } 

    } 

    public static void main(String[] args) 
    { 
     Thread t1 = new T(); 
     Thread t2 = new T(); 
     t1.setName("T1"); 
     t2.setName("T2"); 
     t1.start(); 
     t2.start(); 
    } 
} 

輸出:

PING 
PING 
PONG 
PING 
PING 
PONG 
PING 
PING 
PONG 
PING 
PING 
PONG 
PING 
PING 
PONG 
+1

1000ms遠遠超過OP系統的精確度,大約是30ms,答案是運氣好的球是先開始 – AlexWien

+0

@AlexWien:您使用的是實時操作系統嗎?沒有固定的清理,windows和「正常」的linux授予線程睡眠至少X毫秒。沒有其他的。 – Daniel

+0

@AlexWien:你有沒有在Windows機器上檢查過'System.currentTimeMillis()'的輸出?精度似乎在15-16ms左右。不知道它是否影響'Thread.sleep()',但我不相信它精確到毫秒級別,如果線程需要並行工作,則必須以其他方式進行同步而不是休眠。 – esaj

4

說兩個線程都在0時開始併爲什麼(線程調度)的原因PONG首先啓動。

PONG (now wait 2000) // 0 has elapsed 
PING (now wait 1000) // 0 has elapsed 
PING (now wait 1000) // 1000 has elapsed 
PING (now wait 1000) // 2000 has elapsed 
PONG (now wait 2000) // 2000 has elasped 
... and so on 

如果你想PING秒,然後一個PONG,可以考慮使用一些通知機制(信號量,coutdown鎖存器等)。不要依靠sleep()

0

Thread.sleep()不是之前後移動

System.out.println("\nPING or PONG"); 

或者在啓動它們時在輪胎之間添加Thread.sleep()。

t1.start(); 
Thread.sleep(100); 
t2.start(); 

(但我不喜歡第二個解決方案)

Altough這不是一個專業的解決方案,它應該工作了約30次迭代。 後來,由於它們沒有正確的同步,這不再起作用。

0

首先,您應該使用for循環而不是while循環。它使你的意圖更加清晰。其次,您需要明白線程不保證按照您啓動的順序執行。所有這一切都可以保證,內核將根據底層操作系統的工作方式以及內核將等待的時間,開始爲線程分配時間片至少指定線程休眠的時間量(如果內核支持合併的線程,有時會更少)。

這意味着T1實際上可以在一輪中等待996毫秒,T2可以等待另一個2026毫秒。當我運行這段代碼,例如,我得到了以下的輸出:
PING
PONG
PING
PING
PONG
PING
PING
PONG
PING
PONG
PING
PING
PONG
PING
PING
PONG
PONG
PONG
PONG
PONG

亂七八糟的位。現在,在許多多線程應用程序中,當線程不相互依賴時,這是可以的。但是,如果你希望你的輸出符合你的期望,那麼就像其他答案所暗示的那樣,你需要使用wait()和notify()。

例如,有T1開始,然後有T1開始T2。 T2應該在執行時立即等待,然後T1應在每次PING時通知T2。然後,T2可以計算收到通知的次數,並且只要計數是偶數,它就可以輸出一個PONG。

我將實施作爲練習公開。

0

在這種情況下,通常這是不可能有確定性輸出(除非你是一些奇特的實時技術)。這是由於OS如何管理線程。線程結束脾的事實並不意味着它會立即收到CPU時間。

具有確定性線程執行順序的唯一方法是使用它們之間的同步。使用谷歌瞭解更多,因爲這是非常複雜的事情。

+0

還要注意,即使線程的啓動順序也不是確定性的你已經看到) - 確定性只能通過同步來實現。 – Rafi

相關問題