2011-08-24 111 views
3

我已經寫了一些多線程的愛好程序和一些在我以前的(工程/物理)研究中,所以我認爲我有一個在同步/線程安全和原語,普通用戶發現與JMM和多線程等進行交談。Java:線程共享數據的框架

我發現我需要並且沒有適當的方法來標記不同線程共享的實例或靜態成員類。考慮一下,我們有訪問規則,如私人/保護/公共和關於如何命名getters/setters和許多事情的約定。

但是線程呢?如果我想將變量標記爲線程共享並且遵循特定規則,該怎麼辦? Volatile/Atomic refs可能會完成這項工作,但有時您只需要使用互斥鎖。當你手動記住使用某些東西時......你會忘記它:) - 在某個時刻。

所以我有一個想法,我看到我不是第一個,我也檢查出http://checkthread.org/example-threadsafe.html - 他們似乎有一個相當不錯的代碼分析器,我可能會稍後嘗試哪種讓我做一些事情我想。

但是回到最初的問題。假設我們需要比消息傳遞框架更低級別的東西,並且我們需要比原始互斥體更高級別的東西...我們有什麼...唔...什麼都沒有?

所以基本上,我所做的是一種純Java超線程框架,可以讓您將類成員聲明爲共享或非共享...以及:)。

下面是它如何被使用的例子:

public class SimClient extends AbstractLooper { 

    private static final int DEFAULT_HEARTBEAT_TIMEOUT_MILLIS = 2000; 
    // Accessed by single threads only 
    private final SocketAddress socketAddress; 
    private final Parser parser; 
    private final Callback cb; 
    private final Heart heart; 
    private boolean lookingForFirstMsg = true; 
    private BufferedInputStream is; 
    // May be accessed by several threads (T*) 
    private final Shared<AllThreadsVars> shared = new Shared<>(new AllThreadsVars()); 

. 
. 
. 
. 

    static class AllThreadsVars { 

     public boolean connected = false; 
     public Socket socket = new Socket(); 
     public BufferedOutputStream os = null; 
     public long lastMessageAt = 0; 
    } 

並訪問標記爲線程共享你必須發送一個可運行的樣函子來共享對象的變量:

public final void transmit(final byte[] data) { 
    shared.run(new SharedRunnable<AllThreadsVars, Object, Object>() { 

     @Override 
     public Object run(final AllThreadsVars sharedVariable, final Object input) { 
      try { 
       if (sharedVariable.socket.isConnected() && sharedVariable.os != null) { 
        sharedVariable.os.write(data); 
        sharedVariable.os.flush(); 
       } 
      } catch (final Exception e) { // Disconnected 
       setLastMessageAt(0); 
      } 
      return null; 
     } 
    }, null); 
} 

其中共享運行如下定義:

public interface SharedRunnable<SHARED_TYPE, INPUT, OUTPUT> { 
    OUTPUT run(final SHARED_TYPE s, final INPUT input); 
} 

這是怎麼回事? 好吧,這給了我幫助(是的,你可以泄漏並打破它,但不太可能),我可以將變量集(不僅僅是變量)標記爲線程共享,並且一旦完成,在編譯時保證它不能忘記來同步一些方法)。它還允許我標準化和執行測試,以在編譯時尋找可能的死鎖(儘管atm我只在運行時實現,因爲在編譯時使用上述框架來完成它可能不僅僅需要java編譯器)。

基本上這對我來說是非常有用的,我想知道如果我只是在這裏重新發明輪子,或者這可能是一些我不知道的反模式。我真的不知道該問誰。 (噢和共享 .RUN(SharedRunnable R,INPUT輸入)的作品就像

private final <OUTPUT, INPUT> OUTPUT run(final SharedRunnable<SHARED_TYPE, INPUT, OUTPUT> r, final INPUT input) { 
    try { 
     lock.lock(); 
     return r.run(sharedVariable, input); 
    } finally { 
     lock.unlock(); 
    } 
} 

這只是我自己的實驗,所以它不是一個真正以任何方式完成,但我有一個體面項目現在使用它,它真的幫助了很多。

+0

這裏有問題嗎? – Daniel

+3

我不認爲我理解你的例子,但**共享變量**正是你在處理併發時不想擁有的**。帶鎖的共享變量將使您移動速度變慢,浪費更多時間來等待鎖,一般情況下響應速度較慢並陷入死鎖。達到真正併發的最好方法是根本沒有共享狀態。 –

+0

是的,但是在您的應用程序的某個時刻,如果它需要多個線程,它將需要一些共享變量,無論是消息隊列還是其他東西。我的想法是有辦法構建任意類似原子的操作(並且重要的是:在共享標記的狀態下在編譯時強制使用它們)。此外,我可以毫不費力地爲此寫一個死鎖分析工具,因爲我所有的線程都使用標準化的框架。我已經寫了一個相當簡單的變體,並且可以在我的代碼中檢測到一些潛在的死鎖位置。 – gigurra

回答

4

你的意思是像this?(可通過findbugs等工具強制執行。)

+0

是的,類似的東西,謝謝。使用我自己的,我不需要有一個代碼分析工具來做到這一點,編譯器就足夠了,儘管net.jcip.annotations看起來很不錯:)。在決定是否使用該代碼或我自己的代碼之前,我必須先閱讀合同的工作方式。 – gigurra

0

如果您有值得分享的地方,最好的方法是將其封裝在類中。這樣調用者確實需要知道你使用的線程模型。如果你想知道在內部使用什麼模型,你可以閱讀源代碼,但是調用者不能忘記正確訪問一個ConcurrentMap(例如),因爲它的所有方法都是線程安全的。

+0

問題是,需要同步完成更多的事情,而不僅僅是從預定義的同步集合中添加/刪除。假設你有你的地圖,國旗和時間戳。 public void foo(){ if(flag == ...){ modify(map); setTimeStamp(); flag = newValue; } } 我的想法是將所有這些共享變量和整個操作封裝爲同步。爲了執行同步,所以我不會忘記這麼做,即使在封裝類中也是如此。一個同步的方法是不夠的,你可以忘記。 – gigurra

+1

另外,如果yuo實現封裝類的新行爲或者發佈它,那麼除非在編譯時以某種方式強制執行,否則很容易忘記保持線程安全行爲。我並不是說我的解決方案是最好的,遠非如此,但是以前看到的(見jtahlborn和checkthread)是以非常相似的方式完成的,我不認爲這是完全錯誤的。不過,我可能會嘗試重新設計我的應用程序,所以我不需要這些東西。畢竟消息傳遞可能更好。感謝大家的所有意見。我需要弄清楚現在如何關閉這個問題:)。 – gigurra

+0

如果您有一個使用許多其他組件的組件,而這些組件都必須以線程安全的方式使用,則仍然可以使用封裝來集中管理所有同步要求。這簡化了維護代碼。公開你的鎖定功能更強大,但卻是危險的,這就是爲什麼許多併發庫故意隱藏這種鎖定的原因。 –