2016-07-30 4 views
2

I Synchronisationsblock in syncCmd Funktion haben:warten nicht im Fall synchronisiert Abschnitt belegt ist

public Object Sync = new Object(); 
public void syncCmd(String controlCmd) { 
    synchronized(Sync) { 

     ... 
    } 
} 

Ich brauche eine gewisse Logik, falls hinzufügen, wenn ein Thread Sync beschäftigt hat und seine Arbeit zu tun. In diesem Fall möchte ich "too busy" an System melden und nicht in die Warteschlange kommen. Was ist der beste Weg zu wissen, ob jemand Sync Abschnitt besetzt hat? Wie kann ich wissen, wie viele Threads in diesem Abschnitt warten? Alles ist in Java 1.4.

+0

Hallo, während [dies] (http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) möglicherweise Ihre Frage nicht beantworten kann, glauben Sie, dass es in Ihrem Fall immer noch relevant ist (JDK 1.4), es sei denn, Sie haben dies bereits als Teil Ihrer Implementierung betrachtet. –

+0

Wenn ein Thread in Ihrem Programm kann so lange einen Mutex belegt, dass andere Threads können nicht darauf warten leisten, dann ist das ein Algorithmus, der neu gestaltet werden weint aus. –

Antwort

2

Werfen Sie einen Blick auf die Lock Schnittstelle und ihre Implementierung ReentrantLock. Es ermöglicht Ihnen tryLock() Verfahren, einschließlich der Variante zu verwenden, die für einige Zeit warten können, wenn die Ressource bereits gesperrt ist:

private ReentrantLock lock = new ReentrantLock(); 

public void syncCmd(String controlCmd) { 
    if (lock.tryLock()) { 
     try { 
      // Use your synchronized resource here 
     } finally { 
      lock.unlock(); 
     } 
    } else { 
     // Failed to lock 
    } 
} 

Java 1.4, leider hat keine java.util.concurrency Paket und ich denke, die beste Wahl, die Sie haben, ist zu implementieren, um die gleiche Logik mittels synchronized und Doppelprüfungen:

public class Lock { 
    private final Object lock = new Object(); 
    private volatile boolean locked = false; 

    public boolean tryLock() { 
     if (!locked) { 
      synchronized (lock) { 
       if (!locked) { 
        locked = true; 
        return true; 
       } 
      } 
     } 
     return false; 
    } 

    public void unlock() { 
     synchronized (lock) { 
      locked = false; 
     } 
    } 
} 

es wird nicht so schnell wie ReentrantLock arbeitet, die CAS-Schleife durch den Prozessor Anweisungen in der modernen JVMs unterstützt verwendet, aber es wird die Arbeit erledigen.

Diese Implementierung ist ebenfalls nicht reentrant, Sie können sie erweitern, um den Sperr-Thread zu verfolgen und die Anzahl der Sperren, wenn Sie einen Wiedereintritt benötigen.

Wichtiges Update: @Stephen C machte einen guten Punkt, dass double check is broken in Java 1.4 und man muss immer daran denken. Aber es gibt Ausnahmen. Zum Beispiel kurze primitive Typen. Also, ich denke, es wird in diesem speziellen Fall funktionieren. Weitere Details finden Sie unter "Double-Checked Locking is Broken" Declaration.

+0

Sieht gut aus, aber es ist seit java 1.5 – vico

+0

@Roman, ich stimme zu, dass ich hier schlechte Formulierungen wählte. Es muss nicht verwendet werden, ich werde den Kommentar bearbeiten. Es hängt von der gewählten Fairness-Politik ab, aber im Allgemeinen spielt es alleine. –

+0

@vico, du hast Recht, ich habe meine Antwort erweitert. –

1

Synchronisierte Blöcke/Methoden und primitive Mutexe können das in Java nicht.

Wenn Sie jedoch stattdessen Lock verwenden (javadoc), können Sie tryLock verwenden, um entweder nie zu blockieren oder nur für eine begrenzte Zeit zu blockieren.

Beispiel:

Lock l = new ReentrantLock(); 
if (l.tryLock()) { 
    try { 
     // access the resource protected by this lock 
    } finally { 
      l.unlock(); 
    } 
else { 
    // report "too busy" 
} 

Aber beachten Sie, dass es wichtig ist, zu verwenden „try ... finally“ und ein expliziter unlock() Anruf, um sicherzustellen, dass die Sperre immer freigegeben. (Im Gegensatz zu den synchronized Konstrukte, die kümmert sich das für Sie automatisch.)

Vor Java 1.5 gibt es keine Lösung, die ich in reinem Java kenne. Es könnte mit nativen Codetricks möglich sein, aber ich weiß nicht wie.


Sie/Ihre Management-Unterstützung für Java 1.4, in Ihre Produkte zu Graben suchen sollte und weg von jedem Produkt von Drittanbietern zu migrieren, die auf ihm abhängt. Java 1.5 selbst wurde vor vielen Jahren EOL'd. In der Tat wurden alle Versionen vor Java 1.8 EOL'd; siehe das Dokument Oracle Java SE Support Roadmap.

0

testen (zwei Klassen - Executor und Tracker):

Zieher:

package com.example.so.jdk1_4.synch; 

import java.util.ArrayList; 
import java.util.Date; 
import java.util.List; 
import java.util.Random; 


/** 
* <p> For http://stackoverflow.com/questions/38671520/not-wait-in-case-synchronized-section-is-occupied </p> 
* @author Ravindra HV 
*/ 
public class InUseExample { 

    public synchronized void execute(String command) { 

     InUseTracker.obtainClassInstance().setInuse(true); 

     try { 
      System.out.println("Executing :"+command); 
      Thread.sleep(1000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     }// do work 

     InUseTracker.obtainClassInstance().setInuse(false); 
    } 


    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     System.out.println("Start :"+new Date()); 
     testInUseExample(); 
     System.out.println("Final wait count :"+InUseTracker.obtainClassInstance().waitCount()); 
     System.out.println("End :"+new Date()); 
    } 


    private static void testInUseExample() { 
     final InUseExample inUseExample = new InUseExample(); 

     Runnable runnable = new Runnable() { 
      @Override 
      public void run() { 

       try { 
        InUseTracker.obtainClassInstance().incrementWaitCount(); 
        while(true) { 
         if(InUseTracker.obtainClassInstance().isInuse() == false) { // reduces the chances of this thread going to a block mode.. 
          inUseExample.execute(Thread.currentThread().getName()); 
          break; 
         } 
         else { 
          try { 
           Random random = new Random(); 
           String message = Thread.currentThread().getName()+" - block in use by :"+InUseTracker.obtainClassInstance().getInUseBy(); 
           message = message+" "+". Wait Count :"+InUseTracker.obtainClassInstance().waitCount(); 
           System.out.println(message); 
           Thread.sleep(random.nextInt(1000)); 
          } catch (InterruptedException e) { 
           e.printStackTrace(); 
          } 

         } 
        } 

       } catch (Exception e) { 
        e.printStackTrace(); 
       } finally { 
        InUseTracker.obtainClassInstance().decrementWaitCount(); 
       } 

      } 
     }; 

     int threadCount = 10; 
     List<Thread> threadPoolTemp = new ArrayList<Thread>(); 
     for(int i=0;i<threadCount;i++) { 
      Thread thread = new Thread(runnable); 
      threadPoolTemp.add(thread); 
     } 

     for (Thread thread : threadPoolTemp) { 
      thread.start(); 
     } 

     for (Thread thread : threadPoolTemp) { 
      try { 
       thread.join(); // wait until all threads have executed.. 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 

    } 



} 

Tracker:

package com.example.so.jdk1_4.synch; 

/** 
* <p> For http://stackoverflow.com/questions/38671520/not-wait-in-case-synchronized-section-is-occupied </p> 
* @author Ravindra HV 
*/ 
public class InUseTracker { 

    private boolean inuse; 
    private int waitCount; 
    private String inUseBy; 

    private static InUseTracker DEFAULT_INSTANCE = new InUseTracker(); 

    private InUseTracker() { 
    } 

    public static InUseTracker obtainClassInstance() { 
     return DEFAULT_INSTANCE; 
    } 


    public synchronized boolean isInuse() { 
     return inuse; 
    } 

    public synchronized void setInuse(boolean inuse) { 
     this.inuse = inuse; 
     if(inuse) { 
      setInUseBy(Thread.currentThread().getName()); 
     } 
     else { 
      setInUseBy(""); 
     } 

    } 

    private void setInUseBy(String inUseBy) { 
     this.inUseBy = inUseBy; 
    } 

    public synchronized String getInUseBy() { 
     return inUseBy; 
    } 

    public synchronized void incrementWaitCount() { 
     waitCount++; 
    } 

    public synchronized void decrementWaitCount() { 
     waitCount--; 
    } 

    public synchronized int waitCount() { 
     return waitCount; 
    } 

} 

PS: Erraten Sie die

bewegen müss
InUseTracker.obtainClassInstance().setInuse(false); 

innerhalb eines endlich, wenn oder wie es angemessen ist.

1

Zwei der Antworten oben sprachen über java.util.concurrent.locks.ReentrantLock, aber es existiert nicht in Java 1.4.

Schade, so traurig?

Nein! Wenn Systembibliotheken und Bibliotheken von Drittanbietern Ihnen nicht das liefern, was Sie wollen, dann schreiben Sie es selbst!

Der folgende Code tut, was Sie für bat und absolut nichts mehr. Ich persönlich würde es nicht verwenden, ohne zuerst einige Funktionen hinzuzufügen, die es brauchbarer, testbarer und vor allem narrensicherer machen würden.

Ich biete es nur Ihnen als ein Beispiel wo beginnen.

public class ExtremelySimplisticNonReentrantLock { 
    boolean isLocked = false; 

    /** 
    * @return true if the lock was acquired, false otherwise. 
    */ 
    public synchronized boolean tryToAcquire() { 
     if (isLocked) { 
      return false; 
     } 
     isLocked = true; 
     return true; 
    } 

    public synchronized void release() { 
     lsLocked = false; 
    } 
} 

Teilen und genießen!

Verwandte Themen