2017-04-30 46 views
0

需要使用多線程在數據庫中插入數據,但即使單個線程未能提交,所有事務也必須回滾。試圖通過以下方法解決此問題。處理多線程環境中的事務

在線程之間共享連接對象,並使用join()等待子線程完成,但是這看起來像壞設計,因爲我在線程間共享連接對象。

有人可以建議更好的設計來解決這個問題(不知道我應該去分佈式txn經理)嗎?

+0

在父線程中使用CountDownLatch https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html。 還可以使用savePoint和rollback來控制回滾並監視事務進度https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html – Saurabh

+0

savePoint和rollback在連接級別上工作,如果我使用多重連接,這隻會回滾特定的txn,而不是所有正在其他線程中執行的txn。 – RE350

+0

我明白了,那就要求使用一些事務管理器。如果你使用它作爲應用程序服務器,那麼JBoss提供了一個,但是它也將你的應用程序綁定到JBoss。我不記得它是正確的,但Spring也有一些TM產品。 – Saurabh

回答

2

我建議將來自多個線程的所有SQL操作排列在某些中間數據結構中,然後從單個線程放入數據庫。可以使用線程安全的中間結構,如ConcurrentHashMap,ConcurrentLinkedQueue,或者您可以在使用它時進行同步。

這樣你甚至不需要事先啓動交易。掛起的數據可能不太安全,但我認爲它們在數據庫中並不安全,而事務尚未提交。

當然,這隻有在您沒有select語句從同一事務中選擇未提交的事務數據時才能使用。以這種或那種方式擺脫這種查詢可能需要重新設計。

使用CountDownLatch來檢測所有數據何時準備就緒,並且數據庫寫入線程應該開始其動作。如果從未發生,請使用reactor模式作爲數據庫寫入線程。

1

這裏是我最新的想法與可能的實現步驟:

  • 你父處理線程將(1)創建新的線程做並行數據庫插入,(2)創建一個CountDownLatch對象(這將持有你的父線程,直到所有正在做數據庫插入的子線程完成)(3.)創建一個自動提交模式爲FALSE的數據庫連接對象。
    • 假設你正在產卵6個線程做並行數據庫插入,那麼你將創建CountDownLatch對象這樣CountDownLatch countDownLatch = new CountDownLatch(6),然後產卵的並行線程,做countDownLatch.await()
  • 您的並行線程將開始插入到數據庫,但關鍵是他們每個人都在使用由父線程提供的自動提交FALSE模式的數據庫連接對象,所以基本上沒有子線程會執行數據庫提交。
    • 一旦完成每個子線程,它們將執行countDownLatch.countDown();來減少鎖存計數器。
    • 請注意,您需要提供countDownLatch以及db連接對象到每個線程,我相信你會知道如何。
  • 一旦鎖存計數器爲0,你的父母線程執行將重新開始(直到鎖存計數器不爲0,countDownLatch.await()將持有的線程),然後(1)你可以決定是否根據提交或不從每個線程結果(2.)關閉連接對象。現在,Runnable不會返回任何東西,所以最好使用Callable,以便每個線程都可以通知其狀態。

如果你使用的是Spring,那麼它可以通過它的事務特性來簡化你的工作,但這會變成不同的故事。


現在,什麼你在你的問題中提到幾點 - 你提到的「即使單個線程未能提交,所有的交易必須回滾」,基本上如果你的任何數據庫插入/接入失敗,則你不想承諾任何事情,所以你的Callable將返回他們的執行狀態,我不知道你可能意味着什麼,但我認爲如果你有點關於Callable那麼你應該沒問題。另外,你提到了「,但是這看起來像壞的設計,因爲我在線程間共享連接對象。」「,你將需要共享數據庫連接對象,因爲一旦事務被提交,你就不能回滾,所以你不想回滾要共享連接對象,那麼可能需要有一組SQL語句才能撤銷先前數據庫訪問及其提交所完成的工作。