2016-07-20 16 views
0

Ich muss Klassenobjekte sperren (die eine Socket-Verbindung behandelt) und diese Objekte an Threads zur Verarbeitung übergeben.Java-Locking in einem Thread, Entsperren in einem anderen

public class Client{ 

    protected Socket socket; 
    ... 
    public Lock lock = new ReentrantLock(); 

} 

Ich habe eine einfache Arbeiterklasse, die die Arbeit behandelt.

Dann habe ich eine Schleife, die Client freigeschaltet, sperrt es und übergibt es an Thread.

Client currentClient = null; 
while(true){ 
    for(Client client : clients){ 
     if(client.lock.tryLock()){ 
      // we have the lock 
      currentClient = client; 
      break; 
     } 
    } 
    if(currentClient != null){ 
     break; 
    } 
} 
new Thread(new ClientWriterWorker(currentClient, "some data")).start(); 

Ist es möglich, Sperren in einem anderen Thread zu entsperren? Ist mein Design fehlerhaft?

+0

Auch wenn Java das erlaubt, geht es gegen das Prinzip einer Sperre. Eine Sperre soll von einem Thread gehalten werden, der Dinge an ihr Objekt anlegt, und wenn das erledigt ist, gibt das Objekt das Objekt frei, damit andere Threads dasselbe tun – Jeeter

Antwort

2

Die Verhaltensspezifikation von ReentrantLock gibt an, dass sie von dem Thread entsperrt werden muss, der sie gesperrt hat. Dies ist sinnvoll, da der Zweck dieser Sperrimplementierung darin besteht, zu verfolgen, welcher Thread sie gesperrt hat, sodass sie beliebig oft wieder gesperrt werden kann (Reentrancy). Zitiert Teile der Dokumentation:

public void unlock() 

Versuche, diese Verriegelung zu lösen.

Wenn der aktuelle Thread der Inhaber dieses Locks ist, wird der Hold Count verringert. Wenn der Haltezähler jetzt Null ist, wird die Sperre aufgehoben. Wenn der aktuelle Thread nicht der Inhaber dieser Sperre ist, wird IllegalMonitorStateException ausgelöst.


Es ist durchaus möglich, dass Sie Ihre eigenen Lock Klasse zu implementieren, die von jedem Thread verriegelt und entriegelt werden kann. Hier ist eine Teilantwort:

public class MyLock implements java.util.concurrent.locks.Lock { 

    private boolean isLocked = false; 

    public synchronized void lock() throws InterruptedException { 
     while (isLocked) 
      wait(); 
     isLocked = true; 
    } 

    public synchronized void unlock() { 
     if (!isLocked) 
      throw new IllegalStateException(); 
     isLocked = false; 
     notify(); 
    }  

} 
0

Es sieht für mich wie Sie Ihr Problem überdenken sollte.

Häufig verwenden Sie Pools von etwas (Gewinde, Anschlüsse, was auch immer) und wann immer ein etwas benötigt wird, ein Aufruf zum Pool-Provider-Objekt gibt einen Verweis auf das gewünschte Objekt aus. Bis dieses Objekt an den Pool zurückgegeben wird (durch Aufrufen der Entsperrungsmethode), ist keine weitere Sperrung erforderlich, da die gesamte Idee darin besteht, dass das Objekt jetzt ausschließlich dem Thread gehört, der es aus dem Pool angefordert hat (und der Pool nicht angibt) das Objekt erneut auslesen, bevor es vom Eigentümer Thread zurückgegeben wurde).

In diesem Muster wird normalerweise eine einzelne Sperre (für den Pool) implementiert, die einfach durch Synchronisierung des Poolobjekts (oder eines dedizierten Sperrobjekts) implementiert wird. Die einzigen Methoden, die Sperren und Entsperren durchführen, befinden sich alle im Pool (require() und release()). Die verwalteten Objekte selbst enthalten keine Sperren.

In der einfachsten Versionen es könnte wie folgt aussehen:

class MyResource { 
    // represents the managed resources 
} 

class MyPool { 
    private List<MyResource> available = new ArrayList<>(); 

    public synchronized MyResource obtain() { 
     MyResource result = available.isEmpty() 
      ? new MyResource() : available.remove(0); 
     return result; 
    } 

    public synchronized void release(MyResource resource) { 
     available.add(resource); 
    } 
} 

Offensichtlich ich den gesamten Code Verwendung weggelassen den Pool, um tatsächlich zu initialisieren und Sie können den Fall behandeln wollen, dass keine Ressource dort verfügbar ist anders (zB indem man wartet, bis man verfügbar ist, anstatt ein neues zu erstellen).

Eine Variante davon ist, dass die Ressourcenklasse einen Verweis auf ihren Pool hat und die release() -Methode auf die Ressourcenklasse verschiebt; Das wiederum ruft eine nichtöffentliche Methode im Pool auf, um die eigentliche Veröffentlichung durchzuführen.

Mit ein wenig Planungsaufwand können Lock-Free-Versionen dieses Musters mit der compareAndSet() - Methode von AtomicReference implementiert werden, um den Poolinhalt zu verwalten, ohne irgendwo synchronisiert zu werden.

Verwandte Themen