Advanced   Java   Services Semaphor


Grundlagen

Das Wort Semaphor (das oder der Semaphore, Plural die Semaphore) kommt aus dem Griechischen, und heißt wörtlich übersetzt Zeichenträger. Übliche Übersetzungen sind Signal, Signalgeber oder Ampel. In der Informatik bedeutet Semaphor eine Kontrollinstanz, die sicherstellt, daß nur eine bestimmte Anzahl von Threads auf ein Objekt bzw, Resource zugreifen können. In Java wird diese Kontrollinstanz durch die von Doug Lea entwickelte Klasse java.util.concurrent.Semaphore realisiert (ab Java 1.5).

Ein Semaphor erlaubt einer festen und damit begrenzten Anzahl an Threads eine Resource zu verwenden (kritischer Abschnitt). Die Threads können sich während der Laufzeit ändern. Jeder Thread muß eine Erlaubnis einholen. Ist die maximale Anzahl erreicht, gibt es keine Erlaubnis mehr bis ein zugelassener Thread seine Arbeit beendet hat. Das Semaphor verwaltet intern eine Menge sogenannter Erlaubnisse (engl. permits).


Beispiel

Im folgenden gibt es einen Worker, der eine Ausgabe auf die Konsole machen will. Diese dauert unterschiedlich lange. Jeder Worker muß dazu die Erlaubnis von einer Semaphor erhalten. Es dürfen maximal 3 Worker die Konsole benutzen. Jeder Worker will die Konsole 5-mal verwenden. Jeder Worker zeigt an, zum wievielten Male er die Konsole verwendet. Es werden 5 Worker angelegt.

Der Worker

package hms;

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 */
public class Worker implements Runnable
{
  private Random rd = new Random();
  private Semaphore semaphore;

  public Worker(Semaphore semaphore)
  {
    this.semaphore = semaphore;
  }

  @Override
  public void run()
  {
    boolean acquired = false;

    for(int i = 0; i < 5; i++)
    {
      try
      {
        semaphore.acquire();
        acquired = true;
        System.out.print(Thread.currentThread().getName() + " #" + i + "  : ");
        System.out.println("remaining Permits :" + semaphore.availablePermits());
        TimeUnit.MILLISECONDS.sleep(500 + rd.nextInt(2500));
        System.out.println(Thread.currentThread().getName() + " #" + i + "  : leaving");
      }
      catch(InterruptedException e)
      {
        break;
      }
      finally
      {
        if (acquired) semaphore.release();
      }
    }
  }
}

Main

package hms;

import java.util.concurrent.Semaphore;

public class Semaphor_1
{
  public static void main(String[] args)
  {
    System.out.println("Semaphor_1");

    Semaphore semaphore = new Semaphore(3);
    //Semaphore semaphore = new Semaphore(3, true);
    System.out.println("is fair         : " + semaphore.isFair());
    System.out.println();

    Worker[] workers = new Worker[5];
    for(int i=0; i < workers.length; i++)
      workers[i] = new Worker(semaphore);

    for(int i=0; i < workers.length; i++)
      new Thread(workers[i]).start();
  }
}

Eine mögliche Ausgabe (nicht fair)

is fair         : false

Thread-0 #0  : remaining Permits :2
Thread-1 #0  : remaining Permits :1
Thread-2 #0  : remaining Permits :0
Thread-2 #0  : leaving
Thread-2 #1  : remaining Permits :0
Thread-0 #0  : leaving
Thread-0 #1  : remaining Permits :0
Thread-1 #0  : leaving
Thread-1 #1  : remaining Permits :0
Thread-2 #1  : leaving
Thread-2 #2  : remaining Permits :0
Thread-1 #1  : leaving
Thread-1 #2  : remaining Permits :0
Thread-0 #1  : leaving
Thread-0 #2  : remaining Permits :0
Thread-2 #2  : leaving
Thread-2 #3  : remaining Permits :0
Thread-1 #2  : leaving
Thread-1 #3  : remaining Permits :0
Thread-2 #3  : leaving
Thread-2 #4  : remaining Permits :0
Thread-0 #2  : leaving
Thread-0 #3  : remaining Permits :0
Thread-1 #3  : leaving
Thread-1 #4  : remaining Permits :0
Thread-2 #4  : leaving
Thread-3 #0  : remaining Permits :0
Thread-1 #4  : leaving
Thread-4 #0  : remaining Permits :0
Thread-0 #3  : leaving
Thread-0 #4  : remaining Permits :0
Thread-4 #0  : leaving
Thread-4 #1  : remaining Permits :0
Thread-0 #4  : leaving
Thread-3 #0  : leaving
Thread-3 #1  : remaining Permits :1
Thread-4 #1  : leaving
Thread-4 #2  : remaining Permits :1
Thread-3 #1  : leaving
Thread-3 #2  : remaining Permits :1
Thread-3 #2  : leaving
Thread-3 #3  : remaining Permits :1
Thread-4 #2  : leaving
Thread-4 #3  : remaining Permits :1
Thread-3 #3  : leaving
Thread-3 #4  : remaining Permits :1
Thread-4 #3  : leaving
Thread-4 #4  : remaining Permits :1
Thread-3 #4  : leaving
Thread-4 #4  : leaving

Eine mögliche Ausgabe (fair)

is fair         : true

Thread-0 #0  : Thread-1 #0  : remaining Permits :1
remaining Permits :1
Thread-2 #0  : remaining Permits :0
Thread-1 #0  : leaving
Thread-3 #0  : remaining Permits :0
Thread-3 #0  : leaving
Thread-4 #0  : remaining Permits :0
Thread-0 #0  : leaving
Thread-1 #1  : remaining Permits :0
Thread-2 #0  : leaving
Thread-3 #1  : remaining Permits :0
Thread-1 #1  : leaving
Thread-0 #1  : remaining Permits :0
Thread-4 #0  : leaving
Thread-2 #1  : remaining Permits :0
Thread-3 #1  : leaving
Thread-1 #2  : remaining Permits :0
Thread-0 #1  : leaving
Thread-4 #1  : remaining Permits :0
Thread-2 #1  : leaving
Thread-3 #2  : remaining Permits :0
Thread-1 #2  : leaving
Thread-0 #2  : remaining Permits :0
Thread-4 #1  : leaving
Thread-2 #2  : remaining Permits :0
Thread-3 #2  : leaving
Thread-1 #3  : remaining Permits :0
Thread-0 #2  : leaving
Thread-4 #2  : remaining Permits :0
Thread-1 #3  : leaving
Thread-3 #3  : remaining Permits :0
Thread-2 #2  : leaving
Thread-0 #3  : remaining Permits :0
Thread-4 #2  : leaving
Thread-1 #4  : remaining Permits :0
Thread-0 #3  : leaving
Thread-2 #3  : remaining Permits :0
Thread-3 #3  : leaving
Thread-4 #3  : remaining Permits :0
Thread-2 #3  : leaving
Thread-0 #4  : remaining Permits :0
Thread-1 #4  : leaving
Thread-3 #4  : remaining Permits :0
Thread-4 #3  : leaving
Thread-2 #4  : remaining Permits :0
Thread-3 #4  : leaving
Thread-4 #4  : remaining Permits :0
Thread-0 #4  : leaving
Thread-2 #4  : leaving
Thread-4 #4  : leaving