2017-03-20 103 views
1

我希望你們都做得很好。所以來到這個問題。我的這部分代碼:當線程處於活動狀態時(Runnable)狀態不正常

private static ArrayList<String> primelist = new ArrayList<>(); 
static void addToList(String list_elm) { 
    primelist.add(list_elm); 
} 

基本上這個名單是由我在全碼(見下文)創建的線程的倍數併發訪問,而這些都是負責做一些計算和返回結果然後通過調用方法addToList(String list_elm)將其添加到此primelist

但是,在所有線程被終止(即:完成他們的工作)之後,在primelist中存在一些空值。因此經過一番研究,結果發現ArrayList不是一個線程安全的類,因此也不是它的方法。我想問一下以下(也許很深)的問題:

當線程執行一行代碼時它會被放入(等待//等待的時間)中,也就是說它調用方法addToList(String list_elm)並且它到達了primelist.add(list_elm);行,但是在添加元素時恰好停止!

如果不是,請您澄清我對(特別是)ArrayList案件的困惑。 ((基本上是怎麼回事^^)?)

全碼:

import java.util.ArrayList;  
    import java.util.Iterator; 

    public class CreatingAThreadThree 
    { 
    private static ArrayList<String> primelist = new ArrayList<>(); 
    static void addToList(String list_elm) 
    { 
     primelist.add(list_elm); 
    } 
    static ArrayList<String> getListReference(){ 
     return primelist; 
    } 
    public static void main(String[] args) 
    { 
     for(long x = 6223372036854775899L; x<=(6223372036854775999L); x+=2) 
     { 
      new Thread (new MyCalcRunnable(x)).start();    
     } 
     for(long x = 9223372036854774703L; x<=9223372036854774789L; x+=2) 
     { 
      new MyCalcThread(x, "myChildThread"+x);    
     } 

     Thread mainThread = Thread.currentThread(); 
     int spinner =0; 
     char animation = ' '; 
     System.out.println("Total number of active threads: " + Thread.activeCount()); 
     System.out.print("Calculating primes: "); 
     while(Thread.activeCount() >1) 
     { 
      spinner ++; 
      switch(spinner) 
      { 
      case 1: 
       animation = '|'; 
       break; 
      case 2: 
       animation = '/'; 
       break; 
      case 3: 
       animation = '-'; 
       break; 
      case 4: 
       animation = '\\'; 
       spinner = 0; 
       break; 
      } 
      System.out.print("\b" + animation); 
      try 
      { 
       Thread.sleep(200); 
      }catch(InterruptedException ex) 
      { 
      }   
     } 
     System.out.println("Total number of active threads: " + Thread.activeCount()); 
     System.out.println("Results List:"); 
     Iterator<?> iterator = (getListReference().iterator()); 
     while(iterator.hasNext()) 
     { 
      System.out.println(iterator.next()); 
     } 
    } 
    } 

    class MyCalcThread extends Thread 
    { 
    private long numberToFactor = 0; 
    MyCalcThread(long numberToFactor, String name) 
    { 
     super(name); 
     this.numberToFactor = numberToFactor; 
     start(); 
    } 

    @Override 
    public void run() 
    { 
     CreatingAThreadThree.addToList(new PrimeStuff().isItPrime(this.numberToFactor)); 
    } 
    } 

    class MyCalcRunnable implements Runnable 
    { 
    private long numberToFactor = 0; 
    MyCalcRunnable(long numberToFactor) 
    { 
     this.numberToFactor = numberToFactor; 
    } 
    @Override 
    public void run() 
    { 
    CreatingAThreadThree.addToList(new PrimeStuff().isItPrime(this.numberToFactor)); 
    } 
    } 

    class PrimeStuff 
    { 
    String isItPrime(long numberToFactor) 
    { 
     if(numberToFactor % 2 == 0)   
      return (numberToFactor +"is Not prime....divisible by 2"); 

     long squareRoot = (long)(Math.sqrt(numberToFactor)); 
     for(long i=3; i<squareRoot; i++) 
     { 
      if(numberToFactor % i == 0) 
      { 
       return (numberToFactor +"is Not prime....first divisible by " +     i);  
      } 
     } 
     return (numberToFactor + " is Prime!!"); 
    } 
    } 
+0

這裏有一點需要注意:您想了解一些關於java編碼風格的約定。例如,你不要在普通變量名稱中使用「_」;這些僅適用於SOME_CONSTANT。 – GhostCat

+0

據我所知,在所有字母都是大寫的情況下,常量中使用的是「_」,這在我的代碼中並不是這樣。 –

+0

我的意思是:_僅用於常量。例如,它不像你的list_elem那樣使用。 – GhostCat

回答

1

你正專注於錯誤的問題;意思是:投入你的時間來修復你破損的代碼。

當您有多個線程訪問相同的共享,未受保護的數據;各種各樣的事情都可能發生。

另外:只是改變你使用的列表的類型可能是不夠的。你看,CopyOnWrite列表保證了單個操作的「線程安全」。但是當你有東西像

if (someList.size() > 1) { 
    do something with your list 

仍然不安全;即使在使用CopyOnWrite列表時也是如此 - 因爲列表中有兩個調用;並且該列表可以在第一次和第二次通話之間改變;當其他線程同時更改列表時。

長話短說:一個解決方案是對那些方法,您做希望並行運行使用​​。

換句話說:添加你需要的保護;而不是通過理解線程狀態模型的細節來讓自己感到困惑 - 該部分不會幫助您編寫正確的代碼。

給出你的評論:你試圖在非常「低」的水平上解決這個問題。你認爲你必須理解線程狀態,等待條件等等,才能找到「好」的解決方案。但這不是一種有效的方法;尤其是當你是新手和學習這些東西的時候。

首先您應該擔心會出現正確的解決方案。然後你可以前進並加強它;例如通過做不同的實驗。從這個意義上說,你應該明白:在真正的多線程中,我們通常從這樣的低級細節中嘗試摘要。相反,我們甚至引入附加層,例如Executors

我想告訴你的是:尋找低層次的細節很可能不會幫助你,但是在這一點上你負擔過重。

+0

thx,我做感謝你在這方面的幫助。但是,你看到我在那個代碼中猜測唯一的問題是調用該方法向ArrayList添加元素。至於其他的操作,它們都必須在一個線程生效後執行,這基本上是主線程(這是根據我當前知識的限制),除非另有說明。最後我想說,我想了解線程模型,以便我能夠預測哪裏需要實現更好的更清潔和更安全(只需要(不多不少))代碼所需的保護。 –

+0

查看我的更新。 – GhostCat

+0

再次感謝你幫助我。我之前實際上已經研究了大部分這些基本主題,現在我正在努力回顧並深入探討主題......至於上面的代碼,這不是一個完美的主題,而且我確實意識到如何解決這個問題,但我認爲更好地瞭解幕後發生的事情是一個好主意。畢竟,我絕對還是一個新手^^,但對於這些不符合慣例的命名變量,我只是沒有把注意力放在!!,謝謝你的偉大建議。 –

0

競爭條件可以發生在任何地方,不僅在您編寫的代碼中,而且在ArrayList的實現中。

添加到ArrayList中,即使在最簡單的形式將涉及以下操作:

  1. 閱讀size變量
  2. 將數據寫入elementData[size]
  3. 增量size變量

加入當操作增加的大小要大於elementData的長度,調整大小也會發生:

  1. 使以1.5倍的新的數組一樣大電流elementData
  2. elementData數據的值複製到新的數組
  3. 分配新數組elementData
  4. 寫入elementData[size]
  5. 遞增size變量

In你的情況,如果你將null添加到列表中,那麼有可能是兩個線程試圖同時調整數組的大小。假設目前你的清單有10個元素。和elementData是長度10的所以加入11值將調整elementData

  1. 線索1產生長度爲15的一個新的數組X
  2. 線程1個拷貝從elementData數據到新的數組
  3. 線程2創建一個長度15的一個新的列Y
  4. 螺紋2份從elementData數據到新的數組
  5. 線程1受讓人X到elementData
  6. 線程1寫入值elementData[10]
  7. 線程1倍的增量size至11
  8. 線程2分配y以elementData
  9. 線程2寫入值elementData[11]
  10. 線程2倍的增量size至12
  11. elementData中[ 10]因此將爲空

什麼hap在步驟8中,線程2覆蓋由線程1更改的elementData,因此elementData [10](由線程1寫入)丟失。

相關問題