2014-11-03 103 views
0

我是學習多線程,我有點困惑的幾件事情,多個線程訪問方法

例如

public class Example{ 

    public void function1(){ 
     int toPrint = 0; 
     System.out.println(toPrint); 
     for(int i = 0; i < 10; i++) 
     System.out.println("hello stackOverflow!"); 
    } 
    public syncrhonized void function2(){ 
     ... 
    } 

} 

public static void main(String[] args) { 
      Example example = new Example(); 
      for (int i = 0; i < 10; i++) { 
       RunningThread thread = new RunningThread(example); 
       thread.start(); 
      } 
     } 

像這樣

public class RunningThread{ 
    Example instanceClass; 
    public RunningThread(Example instanceClass){ 
    this.instanceClass = instanceClass; 
    public void run(){ 
     while(true){ 
      instanceClass.function1(); 
      instanceClass.function2(); 
     } 
    } 

現在循環我不能顯示圖像,但我想在我的dubts清楚,所以

如果我開始N個線程,我必須打算這種情況

_______________________________   ______________________________ 
|    thread1   |  |   thread..N   | 
.................................  ................................ 
|   function1   |  |   function1   | 
|   int toPrint = 0;  |  |  int toPrint = 0;  | 
| System.out.println(toPrint); |  | System.out.println(toPrint);| 
| for(int i = 0; i < 10; i++) |  | for(int i = 0; i < 10; i++) | 
| System.out.println();  |  |  System.out.println(); | 
---------------------------------  -------------------------------- 

什麼,我的意思是說,每個線程都有自己的流量(的功能1自己的「副本」),並整理他們將等待執行鎖定function2()後?

_______________________________   
|  thread1 and thread..N |  
.................................  
|   function1   |  
|   int toPrint = 0;  |  
| System.out.println(toPrint); |  
| for(int i = 0; i < 10; i++) |  
| System.out.println();  |  
--------------------------------- 

在這種方式的每個線程shares相同的功能和內容(因此,例如一個線程初始化值和另一個線程不進行初始化)和精加工後,他們將等待執行鎖定的function2()

的執行順序將始終尊重,第一功能1和功能2?

不好意思,如果這麼長,反正先謝謝了。

+0

我在代碼中看不到任何線程。此外,我沒有看到多個線程調用'function2'。 – 2014-11-03 13:12:59

+0

請給我們顯示完整的代碼。 – TheLostMind 2014-11-03 13:13:32

+0

等待,我會插入當我開始線程,但我認爲這是次要的... – user582040 2014-11-03 13:14:29

回答

4

synchronized關鍵字不鎖定功能,但對象,這意味着兩個線程不能同時使用相同的對象。 synchronized void function2只是語法糖

void function2(){synchronized(this){// 

的原因進行同步,即,鎖定的對象是沒有線程可以看到一個對象的狀態下,它的不變量是斷開的。在你的例子中,類Example沒有狀態,因此沒有不變量,這意味着你不需要鎖定它。

你似乎什麼都是function2局部變量予以關注。但是,局部變量永遠不會在線程之間共享,因此每個線程都有自己的每個局部變量的實例。


附錄:如所建議的通過用戶hexafraction,其中需要同步的一個示例:

考慮以下簡單的類:

public class Example { 
    public int count = 0; 

    public void increment() { 
     count++; 
    } 

    public void decrement() { 
     count--; 
    } 
} 

這個類是可變的;其狀態由值count定義。如果客戶撥打incrementdecrement,那麼狀態應該改變。這兩種方法有合同要堅持:

  • increment必須保證的count的值是count加上一個舊值。讓我們count = old(count) + 1

  • Simliarly表示此合同,decrement合同是count = old(count) - 1

我們來運行這個類sequentally:

public static void main(String[] args) { 
    Example sharedData = new Example(); 
    for (int i = 0; i < 1000; i++) 
     sharedData.increment(); 
    System.out.println("Incrementer finished"); 
    for (int i = 0; i < 1000; i++) 
     sharedData.decrement(); 
    System.out.println("Decrementer finished"); 
    System.out.println(sharedData.count); 
} 

它打印:

Incrementer finished 
Decrementer finished 
0 

我們可以像w一樣運行代碼如果想要,結果將始終如一。

讓我們定義使用Example同時相同的實例多線程:

public static void main(String[] args) throws InterruptedException { 
    Example sharedData = new Example(); 
    Thread incrementer = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      for (int i = 0; i < 1000; i++) 
       sharedData.increment(); 
      System.out.println("Incrementer finished"); 
     } 
    }); 
    Thread decrementer = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      for (int i = 0; i < 1000; i++) 
       sharedData.decrement(); 
      System.out.println("Decrementer finished"); 
     } 
    }); 
    incrementer.start(); 
    decrementer.start(); 
    incrementer.join(); 
    decrementer.join(); 
    System.out.println(sharedData.count); 
} 

我們現在有兩個線程:一個增量和遞減器。代碼看起來有點不同,但我們可能期望它達到相同的結果。再次,我們在共享的sharedData上撥打incrementdecrement兩次1000次。但是現在,結果是完全不確定的。多次運行代碼,打印的數字可能是:16, -76, 138, -4

這怎麼可能?我們總是增加一個或者減少一個,但是在兩次1000次之後,我們應該有0的值,對吧?問題是一個線程可能對另一個線程的改變無知。請注意,count++不會發生原子;它與count = count + 1相同,它由讀,計算和寫組成。

考慮以下順序歷史:

incrementer enters increment and reads the value of count, count == 0 
decrementer enters decrement and reads the value of count, count == 0 
incrementer adds one and modifies the state, count == 1 
decrementer subtracts one and modifies the state, count == -1 

注意,通過decrementer計算的狀態變化是基於count的價值,它讀取,即0,這意味着它沒有看到狀態變化由incrementer完成。

有多種方法可以解決此問題,但讓我們試試​​關鍵字。通過鎖定實例,我們可以禁止共享Example實例的併發修改。因此,讓我們修改我們的類:

public class Example { 
    public int count = 0; 

    public synchronized void increment() { 
     count++; 
    } 

    public synchronized void decrement() { 
     count--; 
    } 
} 

有兩種方法鎖定實例是非常重要的,因爲這兩種方法不能看到處於不一致狀態的對象。

如果我們無法修改Example的代碼,例如因爲它是我們使用的庫的一部分,該怎麼辦?我們如何才能使用​​關鍵字來使用多線程的代碼?如前所述,synchronized void increment(){void increment(){synchronized(this)相同,所以同步不是方法的屬性,而是對象。離開Example的代碼不變,我們可以改變我們的客戶端:

public static void main(String[] args) throws InterruptedException { 
    Example sharedData = new Example(); 
    Thread incrementer = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      for (int i = 0; i < 1000; i++) 
       synchronized (sharedData){ 
        sharedData.increment(); 
       } 
      System.out.println("Incrementer finished"); 
     } 
    }); 
    Thread decrementer = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      for (int i = 0; i < 1000; i++) 
       synchronized (sharedData){ 
        sharedData.decrement(); 
       } 
      System.out.println("Decrementer finished"); 
     } 
    }); 
    incrementer.start(); 
    decrementer.start(); 
    incrementer.join(); 
    decrementer.join(); 
    System.out.println(sharedData.count); 
} 
+0

您應該添加一個示例,其中多個方法具有'synchronized'關鍵字。 – hexafraction 2014-11-23 16:53:36

+1

@hexafraction感謝您的反饋。我保留最初的答案,但在兩個同步非常重要的方法中增加了一個示例(和解釋)。 – 2014-11-24 01:28:19

0

既然你做既function1function2​​沒有線程可以離開他們之前執行結束,所以沒有辦法一個線程在功能1的中間停止,而另一個線程執行其功能1或函數2。但是千萬注意,如果同一run()方法有兩個線程(這是我猜的),而第一完成function1調度員或調度可以停止和運行線程2,可同時完成這兩個函數調用,然後thread1可能會繼續。

注意:使用方法(),而該功能()。