Heim  >  Artikel  >  Java  >  Einführung in die Verwendung der drei Hilfsklassen CountDownLatch, CyclicBarrier und Semaphore in Java

Einführung in die Verwendung der drei Hilfsklassen CountDownLatch, CyclicBarrier und Semaphore in Java

不言
不言nach vorne
2019-02-19 15:56:482459Durchsuche

Dieser Artikel bietet Ihnen eine Einführung in die Verwendung der drei Hilfsklassen CountDownLatch, CyclicBarrier und Semaphore. Ich hoffe, dass er für Sie hilfreich ist.

In Java 1.5 werden einige sehr nützliche Hilfsklassen bereitgestellt, die uns bei der gleichzeitigen Programmierung helfen, wie z. B. CountDownLatch, CyclicBarrier und Semaphore. Heute lernen wir die Verwendung dieser drei Hilfsklassen.

1. CountDownLatch-Verwendung

Die CountDownLatch-Klasse befindet sich unter dem Paket java.util.concurrent, mit dem zählerähnliche Funktionen implementiert werden können. Es gibt beispielsweise eine Aufgabe A, die auf den Abschluss der anderen vier Aufgaben warten muss, bevor sie ausgeführt werden kann. Zu diesem Zeitpunkt können Sie CountDownLatch verwenden, um diese Funktion zu implementieren.

Die CountDownLatch-Klasse stellt nur einen Konstruktor bereit:

public CountDownLatch(int count) {  };  //参数count为计数值

Dann sind die folgenden drei Methoden die wichtigsten Methoden in der CountDownLatch-Klasse:

public void await() throws InterruptedException { };   //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { };  //将count值减1

Sehen wir uns unten ein Beispiel an. Es ist klar, wie CountDownLatch verwendet wird:

public class Test {
     public static void main(String[] args) {  
         final CountDownLatch latch = new CountDownLatch(2);
 
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
 
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                     Thread.sleep(3000);
                     System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                     latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
 
         try {
             System.out.println("等待2个子线程执行完毕...");
            latch.await();
            System.out.println("2个子线程已经执行完毕");
            System.out.println("继续执行主线程");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
     }
}

Ausführungsergebnis:

线程Thread-0正在执行
线程Thread-1正在执行
等待2个子线程执行完毕...
线程Thread-0执行完毕
线程Thread-1执行完毕
2个子线程已经执行完毕
继续执行主线程

2. CyclicBarrier-Nutzung

Bedeutet wörtlich: Schleifenzaun, durch den Sie können eine Gruppe von Threads implementieren, die auf einen bestimmten Status warten, bevor sie alle gleichzeitig ausführen. Es wird Loopback genannt, da CyclicBarrier wiederverwendet werden kann, nachdem alle wartenden Threads freigegeben wurden. Nennen wir diesen Zustand vorerst Barriere. Wenn die Methode „await()“ aufgerufen wird, befindet sich der Thread in der Barriere.

Die CyclicBarrier-Klasse befindet sich unter dem Paket java.util.concurrent und stellt zwei Konstruktoren bereit:

public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}

Der Parameter Parties bezieht sich darauf, wie viele Threads oder Aufgaben auf den Barrierezustand warten dürfen ; der Parameter barrierAction ist, wann diese ausgeführt werden, wenn alle Threads den Barrierezustand erreichen.

Dann ist die wichtigste Methode in CyclicBarrier die Wait-Methode, die zwei überladene Versionen hat:

public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };

Die erste Version wird häufiger verwendet und dient dazu, den aktuellen Thread anzuhalten, bis alle Threads danach angehalten sind Beim Erreichen des Barrierezustands werden nachfolgende Aufgaben gleichzeitig ausgeführt.

Die zweite Version besteht darin, diese Threads direkt eine bestimmte Zeit lang warten zu lassen Lassen Sie die Threads, die die Barriere erreicht haben, nachfolgende Aufgaben ausführen.

Ein paar Beispiele helfen Ihnen zu verstehen:

Angenommen, es gibt mehrere Threads, die Daten schreiben müssen, und erst nachdem alle Threads den Datenschreibvorgang abgeschlossen haben, können diese Threads damit fortfahren Folgende Dinge wie: Warten Sie, bis andere Threads den Schreibvorgang abgeschlossen haben.

Wenn alle Threads den Schreibvorgang abgeschlossen haben, führen alle Threads weiterhin nachfolgende Vorgänge aus.

Wenn Sie zusätzliche Vorgänge ausführen möchten, nachdem alle Threads die Schreibvorgänge abgeschlossen haben, können Sie ausführbare Parameter für CyclicBarrier bereitstellen:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有线程写入完毕,继续处理其他任务...");
        }
    }
}

Ausführungsergebnisse:

线程Thread-0正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...

Aus den Ergebnissen: Sie können sehen, dass, wenn alle vier Threads den Barrierezustand erreichen, ein Thread aus den vier Threads ausgewählt wird, um Runnable auszuführen.

Werfen wir einen Blick auf die Auswirkung der Angabe der Zeit für das Warten:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {
            @Override
            public void run() {
                System.out.println("当前线程"+Thread.currentThread().getName());  
            }
        });
 
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有线程写入完毕,继续处理其他任务...");
        }
    }
}

Ausführungsergebnis:

线程Thread-0正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2正在写入数据...
线程Thread-3正在写入数据...
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
当前线程Thread-3
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...

Der obige Code startet absichtlich den letzten Thread in der for-Schleife des Hauptmethode: Nachdem die ersten drei Threads die Barriere erreicht haben, wird nach dem Warten auf die angegebene Zeit und der Feststellung, dass der vierte Thread die Barriere nicht erreicht hat, eine Ausnahme ausgelöst und die nachfolgenden Aufgaben werden weiterhin ausgeführt.

Außerdem kann CyclicBarrier wiederverwendet werden:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
 
        for(int i=0;i<N;i++) {
            if(i<N-1)
                new Writer(barrier).start();
            else {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                new Writer(barrier).start();
            }
        }
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                try {
                    cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
                } catch (TimeoutException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
        }
    }
}

Ausführungsergebnis:

线程Thread-0正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
线程Thread-3正在写入数据...
java.util.concurrent.TimeoutException
Thread-1所有线程写入完毕,继续处理其他任务...
Thread-0所有线程写入完毕,继续处理其他任务...
    at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    at com.cxh.test1.Test$Writer.run(Test.java:58)
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    at com.cxh.test1.Test$Writer.run(Test.java:58)
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    at com.cxh.test1.Test$Writer.run(Test.java:58)
Thread-2所有线程写入完毕,继续处理其他任务...
java.util.concurrent.BrokenBarrierException
线程Thread-3写入数据完毕,等待其他线程写入完毕
    at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    at com.cxh.test1.Test$Writer.run(Test.java:58)
Thread-3所有线程写入完毕,继续处理其他任务...

Wie aus dem Ausführungsergebnis ersichtlich ist, das erste 4 Threads haben die Barriere überschritten. Nachdem der Zustand erreicht ist, kann er für eine neue Nutzungsrunde verwendet werden. CountDownLatch kann nicht wiederverwendet werden.

3. Semaphor-Verwendung

Semaphor wird wörtlich übersetzt als Semaphor kann die Anzahl der Threads steuern, auf die gleichzeitig zugegriffen wird, und eine Berechtigung über acquire() erhalten. Wenn nicht, warten Sie und release() gibt eine Berechtigung frei. Die Semaphore-Klasse befindet sich im Paket java.util.concurrent. Sie stellt zwei Konstruktoren bereit:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
 
        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
 
        try {
            Thread.sleep(25000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        System.out.println("CyclicBarrier重用");
 
        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
 
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
        }
    }
}

Lassen Sie uns über einige der wichtigeren Methoden in der Semaphore-Klasse sprechen () , release()-Methode:

线程Thread-0正在写入数据...
线程Thread-1正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
Thread-0所有线程写入完毕,继续处理其他任务...
Thread-3所有线程写入完毕,继续处理其他任务...
Thread-1所有线程写入完毕,继续处理其他任务...
Thread-2所有线程写入完毕,继续处理其他任务...
CyclicBarrier重用
线程Thread-4正在写入数据...
线程Thread-5正在写入数据...
线程Thread-6正在写入数据...
线程Thread-7正在写入数据...
线程Thread-7写入数据完毕,等待其他线程写入完毕
线程Thread-5写入数据完毕,等待其他线程写入完毕
线程Thread-6写入数据完毕,等待其他线程写入完毕
线程Thread-4写入数据完毕,等待其他线程写入完毕
Thread-4所有线程写入完毕,继续处理其他任务...
Thread-5所有线程写入完毕,继续处理其他任务...
Thread-6所有线程写入完毕,继续处理其他任务...
Thread-7所有线程写入完毕,继续处理其他任务...

acquire() wird verwendet, um eine Berechtigung zu erhalten. Wenn keine Berechtigung eingeholt werden kann, wird gewartet, bis die Berechtigung eingeholt wird.

release() wird verwendet, um die Lizenz freizugeben. Beachten Sie, dass vor der Veröffentlichung eine Genehmigung eingeholt werden muss.

Diese vier Methoden werden blockiert. Wenn Sie das Ausführungsergebnis sofort erhalten möchten, können Sie die folgenden Methoden verwenden:

public Semaphore(int permits) {          //参数permits表示许可数目,即同时可以允许多少线程进行访问
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {    //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
    sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}

Darüber hinaus können Sie auch die Anzahl der verfügbaren Berechtigungen abrufen die Methode availablePermits().

Schauen wir uns die spezifische Verwendung von Semaphore anhand eines Beispiels an:

Wenn eine Fabrik 5 Maschinen, aber 8 Arbeiter hat, kann eine Maschine nur von einem Arbeiter gleichzeitig verwendet werden. Nach der Verwendung können andere Mitarbeiter es weiterhin verwenden. Dann können wir es über Semaphore implementieren:

public void acquire() throws InterruptedException {  }     //获取一个许可
public void acquire(int permits) throws InterruptedException { }    //获取permits个许可
public void release() { }          //释放一个许可
public void release(int permits) { }    //释放permits个许可

Ausführungsergebnis:

public boolean tryAcquire() { };    //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };  //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false

Das Folgende ist eine Zusammenfassung der drei oben genannten Hilfsklassen:

1) CountDownLatch und CyclicBarrier können Beide implementieren das Warten zwischen Threads, haben jedoch unterschiedliche Schwerpunkte:

CountDownLatch wird im Allgemeinen verwendet, damit ein Thread A darauf wartet, dass mehrere andere Threads ihre Aufgaben abschließen, bevor er ihn ausführt.

Und CyclicBarrier wird im Allgemeinen verwendet, damit eine Gruppe von Threads darauf wartet, dass der andere einen bestimmten Wert erreicht Zustand, und dann diese Gruppe Die Threads werden gleichzeitig ausgeführt

Außerdem kann CountDownLatch nicht wiederverwendet werden, CyclicBarrier jedoch.

2) Semaphor ähnelt eigentlich einer Sperre. Es wird im Allgemeinen verwendet, um den Zugriff auf eine bestimmte Gruppe von Ressourcen zu kontrollieren.

Das obige ist der detaillierte Inhalt vonEinführung in die Verwendung der drei Hilfsklassen CountDownLatch, CyclicBarrier und Semaphore in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen