2015-10-14 95 views
0

我只有一個同步方法。其目的是防止兩個線程更新同一個表,這肯定會導致異常。我試圖更新一個數據來自一個巨大的外部數據庫的表。在更新我的表格之前,我必須做一些過濾和清理,這需要花費很多時間。我創建了多個線程,以便我可以分割任務並快速更新表格。只要一個線程完成清理數據,它應該更新我的表,而其他線程仍在清理工作。但是,在我的示例代碼中,線程會覆蓋值,因爲發生關係之前發生。我不希望線程共享我希望他們在更新某個線程時不更新的數據。如果你可以在我的代碼中看到,我創建了一個列表,我用它來循環創建一個線程並從列表中傳遞每個數據。但是,在結果中,線程1,4和5在Synchronized方法中使用相同的programId時,它們不應該是。任何人都可以幫助防止這種情況,以便每個線程將在同步代碼中具有唯一的ID嗎?如何防止在java中的同步代碼發生之前?

這裏是我的代碼:

public Class HomeController{ 

    List<String> programList = new ArrayList<String>(); 
    programList.add("xxx"); 
    programList.add("yyy"); 
    for(String programId: programList){ 

    System.out.println("programId Controller is " + programId); 
    //for each program Id create a thread 
    runnable.setProgramId(programId); 
    taskExecutor.execute(runnable); 
    } 

} 

public class TestRunnable implements Runnable{ 

    public void run(){ 
      String threadName = Thread.currentThread().getName(); 
      System.out.println("threadName and programId Runnable are " + threadName +"--"+ programId); 
      TestSynchronized testSynchronized = new TestSynchronized(); 
      testSynchronized.updateTable(programId); 
      } 
    } 

} 

public class TestSynchronized{ 

    private volatile boolean isLocked = false; 

    public synchronized void updateTable(String programId){ 

      while(isLocked) { 
       try { 
        wait(); 
       } catch (InterruptedException e) {} 
      } 
      isLocked=true; 
      String threadName = Thread.currentThread().getName(); 
      System.out.println("threadName and programId Synchronized are " + threadName +"--"+ programId); 

      isLocked=false; 
      notify(); 
    } 
} 

下面是結果:

programId Controller is 23022490857 
programId Controller is 23022491963 
threadName and programId Runnable are taskExecutor-1--23022490857 
programId Controller is 23022492068 
threadName and programId Runnable are taskExecutor-2--23022491963 
programId Controller is 23022492300 
threadName and programId Synchronized are taskExecutor-1--23022491963 
programId Controller is 23022495561 
threadName and programId Runnable are taskExecutor-3--23022492068 
threadName and programId Synchronized are taskExecutor-2--23022492068 
programId Controller is 38416827134 
threadName and programId Synchronized are taskExecutor-3--23022492300 
threadName and programId Runnable are taskExecutor-4--23022492300 
threadName and programId Runnable are taskExecutor-1--38416827134 
threadName and programId Synchronized are taskExecutor-1--38416827134 
threadName and programId Runnable are taskExecutor-5--23022495561 
threadName and programId Synchronized are taskExecutor-4--38416827134 
threadName and programId Synchronized are taskExecutor-5--38416827134 
+0

您應該爲每個programId創建一個同步測試,以及一個TestRunnable。您不應該重複使用相同的TestRunnable。 –

+0

感謝馬克的快速回復。我怎樣才能防止兩個線程同時更新同一個表,因爲它會導致異常? –

+1

請詢問您的實際問題,因爲該問題與您展示的代碼的問題並無關係。 –

回答

2

你必須在代碼中兩個問題所示:

  1. 正在重用爲每個線程在同一TestRunnable,這意味着programId是(或可以)覆蓋在使用它之前。
  2. 您正在爲每個線程創建新的TestSynchronized,因此您有線程安全。

你需要做的是:

  1. 創建一個,也是唯一一個TestSynchronized
  2. 創建一個新的TestRunnable每個programId。在構造函數中傳遞programId和共享TestSynchronized
+0

馬克,謝謝。它解決了我的問題。感謝您分享您的知識。 –

1

怎樣的TestSynchronized許多副本,你呢?如果它不止一個,你將不會得到任何同步。

與實際代碼執行分開同步。

+0

hi Tassos,每個線程都有自己的實例,但不能同時使用synchronized方法。所以實質上,即使他們有不同的實例,他們也會分享這種方法。 –

+0

你正在混合兩個不同的東西@ user3067802 ...兩個實例的方法(代碼)是相同的。但是同步處理線程......所以兩個線程可以輸入相同的同步方法,只要它們在方法所屬的類的兩個不同實例上執行。 – Fildor

+0

嗨Fildor,你能告訴我哪一個是造成這個問題嗎?我已經分離了Runnable和Synchronized代碼,以確保每個代碼都在做自己的工作。 –

1

您需要了解對象監視器的工作方式。​​將阻止訪問資源,對象引用。在這種情況下,您爲每個線程創建一個新的TestSynchronized。由於同步工作對象引用,你基本上不會阻塞任何地方。

如果您創建一個TestSynchronized並將其傳遞給每個TestRunnable,您將獲得您期望的輸出。

1

...線程覆蓋值,因爲發生關係之前發生。

不是真的回答你的問題,但我想你誤解了「之前會發生」的意思。

Java語言規範(JLS)使用短語「發生在...之前」來描述某些保證,您可以使用它來推理您的程序的行爲。例如,JLS說在一個線程中留下一個synchronized(o)塊,之前發生另一個線程在同一個對象o上同步。這就是你如何知道如果線程A進入塊,更新一些變量,然後離開塊,然後線程B進入塊;線程B保證看到變量中存儲了什麼線程A.

但這裏是它不會告訴你:

它不會告訴你哪個線程會在得到到synchronized塊第一。如果程序中的兩個線程可能試圖同時在同一個對象上進行同步,那麼您的數據競賽。一條線會贏,另一條線會輸,而且無法知道哪一條線會贏。

如果您的程序中有數據競爭,並且重要的是哪個線程獲勝,那麼您的缺陷

您可以改變您的設計,以便哪個線程獲勝並不重要,或者更改您的設計以使用某種更高級別的同步(隊列,信號量,障礙等)來確保權利線程勝。