it-swarm.dev

كيف يتم استخدام CountDownLatch في Java Multithreading؟

هل يمكن لشخص ما أن يساعدني في فهم ماهية Java CountDownLatch ومتى يتم استخدامه؟

ليس لدي فكرة واضحة عن كيفية عمل هذا البرنامج. كما أفهم ، تبدأ جميع المواضيع الثلاثة في آن واحد ، وسيقوم كل مؤشر ترابط بالاتصال CountDownLatch بعد 3000ms. لذلك العد التنازلي ستنخفض واحدا تلو الآخر. بعد أن يصبح المزلاج صفرًا ، يطبع البرنامج "مكتمل". ربما الطريقة التي فهمت غير صحيحة.

import Java.util.concurrent.CountDownLatch;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;

class Processor implements Runnable {
    private CountDownLatch latch;

    public Processor(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        System.out.println("Started.");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        latch.countDown();
    }
}

// ------------------------------------------------ -----

public class App {

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(3); // coundown from 3 to 0

        ExecutorService executor = Executors.newFixedThreadPool(3); // 3 Threads in pool

        for(int i=0; i < 3; i++) {
            executor.submit(new Processor(latch)); // ref to latch. each time call new Processes latch will count down by 1
        }

        try {
            latch.await();  // wait until latch counted down to 0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Completed.");
    }

}
162
amal

نعم ، فهمت بشكل صحيح. CountDownLatch يعمل من حيث المبدأ latch ، سينتظر الخيط الرئيسي حتى يتم فتح البوابة. مؤشر ترابط واحد ينتظر n مؤشرات الترابط ، المحدد أثناء إنشاء CountDownLatch.

أي مؤشر ترابط ، عادةً ما يكون الخيط الرئيسي للتطبيق ، والذي يستدعي CountDownLatch.await() ، سينتظر حتى يصل العدد إلى الصفر أو يقطع بواسطة مؤشر ترابط آخر. جميع مؤشرات الترابط الأخرى مطلوبة للعد التنازلي عن طريق الاتصال بـ CountDownLatch.countDown() بمجرد اكتمالها أو استعدادها.

بمجرد وصول العدد إلى الصفر ، يستمر مؤشر ترابط الانتظار. أحد عيوب/مزايا CountDownLatch هو أنه غير قابل لإعادة الاستخدام: بمجرد أن يصل العدد إلى الصفر ، لا يمكنك استخدام CountDownLatch بعد الآن.

تصحيح:

استخدم CountDownLatch عندما يتطلب مؤشر ترابط واحد (مثل مؤشر الترابط الرئيسي) انتظار مؤشر ترابط واحد أو أكثر ، قبل أن يتمكن من متابعة المعالجة.

مثال كلاسيكي لاستخدام CountDownLatch في Java هو تطبيق Java أساسي من جانب الخادم يستخدم بنية الخدمات ، حيث يتم توفير خدمات متعددة بواسطة سلاسل عمليات متعددة ولا يمكن بدء تشغيل التطبيق حتى يتم تشغيل جميع الخدمات بنجاح.

ملاحظة يحتوي سؤال البروتوكول الاختياري على مثال واضح ومباشر ، لذا لم أدرجه.

175
NikolaB

CountDownLatch في Java هو نوع من المزامن الذي يسمح لواحد Thread بانتظار واحد أو أكثر من Threads قبل أن يبدأ المعالجة.

CountDownLatch يعمل على مبدأ المزلاج ، سينتظر الموضوع حتى يتم فتح البوابة. مؤشر ترابط واحد ينتظر n عدد سلاسل الرسائل المحددة أثناء إنشاء CountDownLatch.

مثلا final CountDownLatch latch = new CountDownLatch(3);

نحن هنا تعيين العداد إلى 3.

أي مؤشر ترابط ، عادةً ما يكون مؤشر الترابط الرئيسي للتطبيق ، والذي يستدعي CountDownLatch.await() ، سينتظر حتى يصل العدد إلى الصفر أو يتم مقاطعته بواسطة Thread آخر. جميع مؤشرات الترابط الأخرى مطلوبة للقيام بالعد التنازلي عن طريق الاتصال بـ CountDownLatch.countDown() بمجرد اكتمالها أو استعدادها للعمل. بمجرد أن يصل العدد إلى الصفر ، يبدأ Thread انتظار.

هنا يتم تقليل العدد بواسطة طريقة CountDownLatch.countDown().

سينتظر Thread الذي يستدعي الأسلوب await() حتى يصل العدد الأولي إلى الصفر.

لجعل عدد صفر مؤشرات الترابط الأخرى تحتاج إلى استدعاء الأسلوب countDown(). بمجرد أن يصبح العدد صفرًا ، سيستأنف مؤشر الترابط الذي استدعى الأسلوب await() (بدء التنفيذ).

عيب CountDownLatch هو أنه غير قابل لإعادة الاستخدام: بمجرد أن يصبح العدد صفرًا ، لم يعد قابلاً للاستخدام.

36
Vishal Akkalkote

أوضح NikolaB ذلك جيدًا ، ومع ذلك قد يكون من المفيد فهم ذلك ، فإليك مثال بسيط ...

 import Java.util.concurrent.*;


  public class CountDownLatchExample {

  public static class ProcessThread implements Runnable {

    CountDownLatch latch;
    long workDuration;
    String name;

    public ProcessThread(String name, CountDownLatch latch, long duration){
        this.name= name;
        this.latch = latch;
        this.workDuration = duration;
    }


    public void run() {
        try {
            System.out.println(name +" Processing Something for "+ workDuration/1000 + " Seconds");
            Thread.sleep(workDuration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+ "completed its works");
        //when task finished.. count down the latch count...

        // basically this is same as calling lock object notify(), and object here is latch
        latch.countDown();
    }
}


public static void main(String[] args) {
    // Parent thread creating a latch object
    CountDownLatch latch = new CountDownLatch(3);

    new Thread(new ProcessThread("Worker1",latch, 2000)).start(); // time in millis.. 2 secs
    new Thread(new ProcessThread("Worker2",latch, 6000)).start();//6 secs
    new Thread(new ProcessThread("Worker3",latch, 4000)).start();//4 secs


    System.out.println("waiting for Children processes to complete....");
    try {
        //current thread will get notified if all chidren's are done 
        // and thread will resume from wait() mode.
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("All Process Completed....");

    System.out.println("Parent Thread Resuming work....");



     }
  }
20
vikashait

يتم استخدامه عندما نريد انتظار أكثر من مؤشر ترابط واحد لإكمال مهمته. وهو مشابه للانضمام في المواضيع.

حيث يمكننا استخدام CountDownLatch

النظر في سيناريو حيث لدينا شرط حيث لدينا ثلاثة مؤشرات ترابط "A" ، "B" و "C" ، ونحن نريد أن نبدأ سلسلة "C" فقط عند اكتمال سلاسل العمليات "A" و "B" أو إكمال المهمة جزئياً.

يمكن تطبيقه على سيناريو تكنولوجيا المعلومات في العالم الحقيقي

فكر في سيناريو يقوم فيه المدير بتقسيم الوحدات بين فرق التطوير (A و B) ويريد تعيينها لفريق QA للاختبار فقط عندما يكمل الفريقان مهمتهما.

public class Manager {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        MyDevTeam teamDevA = new MyDevTeam(countDownLatch, "devA");
        MyDevTeam teamDevB = new MyDevTeam(countDownLatch, "devB");
        teamDevA.start();
        teamDevB.start();
        countDownLatch.await();
        MyQATeam qa = new MyQATeam();
        qa.start();
    }   
}

class MyDevTeam extends Thread {   
    CountDownLatch countDownLatch;
    public MyDevTeam (CountDownLatch countDownLatch, String name) {
        super(name);
        this.countDownLatch = countDownLatch;       
    }   
    @Override
    public void run() {
        System.out.println("Task assigned to development team " + Thread.currentThread().getName());
        try {
                Thread.sleep(2000);
        } catch (InterruptedException ex) {
                ex.printStackTrace();
        }
    System.out.println("Task finished by development team Thread.currentThread().getName());
            this.countDownLatch.countDown();
    }
}

class MyQATeam extends Thread {   
    @Override
    public void run() {
        System.out.println("Task assigned to QA team");
        try {
                Thread.sleep(2000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("Task finished by QA team");
    }
}

إخراج الرمز أعلاه سيكون:

المهمة المسندة لفريق التطوير devB

المهمة المسندة لفريق التطوير devA

انتهت المهمة من قبل فريق التطوير devB

انتهت المهمة من قبل فريق التطوير devA

المهمة المسندة لفريق ضمان الجودة

انتهت المهمة من قبل فريق ضمان الجودة

هنا تنتظر () الطريقة تنتظر علامة countdownlatch لتصبح 0 و countDown () أسلوب تناقصات العلامة countdownlatch بمقدار 1.

قيود JOIN: يمكن أيضًا تحقيق المثال أعلاه مع JOIN ، لكن لا يمكن استخدام JOIN في سيناريوهين:

  1. عندما نستخدم ExecutorService بدلاً من فئة مؤشر الترابط لإنشاء مؤشرات الترابط.
  2. قم بتعديل المثال أعلاه حيث يرغب مدير النظام في تسليم التعليمات البرمجية إلى فريق ضمان الجودة بمجرد اكتمال التطوير لمهمته البالغة 80٪. يعني ذلك أن CountDownLatch يسمح لنا بتعديل التطبيق الذي يمكن استخدامه لانتظار مؤشر ترابط آخر للتنفيذ الجزئي.
17
V Jo

من وثائق Oracle حول CountDownLatch :

أداة مساعدة في المزامنة تسمح لواحد أو أكثر من سلاسل العمليات بالانتظار حتى تكتمل مجموعة من العمليات التي يتم تنفيذها في سلاسل عمليات أخرى.

تتم تهيئة CountDownLatch بعدد محدد. كتلة أساليب await حتى يصل العدد الحالي إلى الصفر بسبب استدعاء الأسلوب countDown() ، وبعد ذلك يتم إصدار جميع مؤشرات انتظار الانتظار وإرجاع أي دعوات لاحقة من انتظار على الفور. هذه ظاهرة طلقة واحدة - لا يمكن إعادة ضبط العد.

CountDownLatch هي أداة مزامنة متعددة الاستخدامات ويمكن استخدامها لعدد من الأغراض.

يخدم CountDownLatch الذي تم تهيئته بعدد واحد بمثابة مزلاج تشغيل/إيقاف بسيط ، أو بوابة: تنتظر جميع سلاسل العمليات التي تستدعي الانتظار الانتظار عند البوابة حتى يتم فتحها من خلال سلسلة عمليات استدعاء countDown ().

يمكن استخدام CountDownLatch تمت تهيئته إلى N لجعل مؤشر ترابط واحد ينتظر حتى تكمل مؤشرات الترابط N بعض الإجراءات ، أو تم إكمال بعض الإجراءات N مرة.

public void await()
           throws InterruptedException

يؤدي مؤشر الترابط الحالي إلى الانتظار حتى يتم عد المزلاج إلى الصفر ، ما لم تتم مقاطعة مؤشر الترابط.

إذا كان العدد الحالي صفرًا ، فتعود هذه الطريقة فورًا.

public void countDown()

يقلل من عدد المزلاج ، مع تحرير كل مؤشرات انتظار الانتظار إذا وصل العدد إلى الصفر.

إذا كان العدد الحالي أكبر من الصفر ، فسيتم تقليله. إذا كان العدد الجديد صفرًا ، فسيتم إعادة تمكين جميع سلاسل انتظار الانتظار لأغراض جدولة سلسلة الرسائل.

شرح مثالك.

  1. لقد قمت بتعيين عدد كـ 3 للمتغير latch

    CountDownLatch latch = new CountDownLatch(3);
    
  2. لقد قمت بتمرير هذا المشترك latch إلى مؤشر ترابط العامل: Processor

  3. تم إرسال ثلاث Runnable مثيلات Processor إلى ExecutorServiceexecutor
  4. مؤشر الترابط الرئيسي (App) ينتظر أن يصبح العدد صفرًا مع العبارة أدناه

     latch.await();  
    
  5. ينام مؤشر الترابط Processor لمدة 3 ثوانٍ ثم يتناقص عدد القيم مع latch.countDown()
  6. أول مثيل Process سيغير عدد المزلاج ك 2 بعد اكتماله بسبب latch.countDown().

  7. مثيل Process الثاني سيغير عدد المزلاج على أنه 1 بعد اكتماله بسبب latch.countDown().

  8. مثيل Process الثالث سيغير عدد المزلاج كـ 0 بعد اكتماله بسبب latch.countDown().

  9. يؤدي صفر صفر على مزلاج إلى ظهور مؤشر ترابط رئيسي App من await

  10. يطبع برنامج التطبيق هذا الإخراج الآن: Completed

2
Ravindra babu

إذا قمت بإضافة بعض التصحيح بعد مكالمتك إلى latch.countDown () ، فقد يساعدك ذلك في فهم سلوكه بشكل أفضل.

latch.countDown();
System.out.println("DONE "+this.latch); // Add this debug

سوف تظهر الإخراج عدد يجري إنقاص. هذا "العد" هو عدد مهام Runnable (كائنات المعالج) التي بدأت ضدها والتي تم استدعاء not countDown لها وبالتالي تم حظر مؤشر الترابط الرئيسي في دعوته إلى latch.await ().

DONE [email protected][Count = 2]
DONE [email protected][Count = 1]
DONE [email protected][Count = 0]
2
natmat

يمكّنك CoundDownLatch من إجراء مؤشر ترابط انتظار حتى تتم كافة مؤشرات الترابط الأخرى مع تنفيذها.

يمكن أن يكون الرمز الزائف:

// Main thread starts
// Create CountDownLatch for N threads
// Create and start N threads
// Main thread waits on latch
// N threads completes there tasks are returns
// Main thread resume execution
2
user2203151

من الأمثلة الجيدة على متى يمكنك استخدام شيء مثل هذا باستخدام Java Simple Serial Connector ، والوصول إلى المنافذ التسلسلية. عادةً ما ستكتب شيئًا ما إلى المنفذ ، وبشكل غير متزامن ، في سلسلة رسائل أخرى ، سوف يستجيب الجهاز على SerialPortEventListener. عادة ، ستحتاج إلى التوقف بعد الكتابة إلى المنفذ لانتظار الاستجابة. تعتبر معالجة أقفال سلاسل المفاتيح لهذا السيناريو يدويًا أمرًا بالغ الصعوبة ، لكن استخدام Countdownlatch سهل. قبل أن تفكر في أنه يمكنك القيام بذلك بطريقة أخرى ، كن حذرًا في ظروف السباق التي لم تفكر فيها أبدًا !!

شبة الكود:

CountDownLatch latch;
void writeData() { 
   latch = new CountDownLatch(1);
   serialPort.writeBytes(sb.toString().getBytes())
   try {
      latch.await(4, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
   }
}
class SerialPortReader implements SerialPortEventListener {
    public void serialEvent(SerialPortEvent event) {
        if(event.isRXCHAR()){//If data is available
            byte buffer[] = serialPort.readBytes(event.getEventValue());
            latch.countDown();
         }
     }
}

2
user2709454

هذا المثال من Java Doc ساعدني في فهم المفاهيم بوضوح:

class Driver { // ...
  void main() throws InterruptedException {
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(N);

    for (int i = 0; i < N; ++i) // create and start threads
      new Thread(new Worker(startSignal, doneSignal)).start();

    doSomethingElse();            // don't let run yet
    startSignal.countDown();      // let all threads proceed
    doSomethingElse();
    doneSignal.await();           // wait for all to finish
  }
}

class Worker implements Runnable {
  private final CountDownLatch startSignal;
  private final CountDownLatch doneSignal;
  Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
     this.startSignal = startSignal;
     this.doneSignal = doneSignal;
  }
  public void run() {
     try {
       startSignal.await();
       doWork();
       doneSignal.countDown();
     } catch (InterruptedException ex) {} // return;
  }

  void doWork() { ... }
}

يتحدث رسمياً:

 enter image description here 

من الواضح ، CountDownLatch يسمح لمؤشر ترابط واحد (هنا Driver) بالانتظار حتى يتم تنفيذ مجموعة من سلاسل العمليات قيد التشغيل (هنا Worker) مع تنفيذها.

1
Saurav Sahu

كما ذكر في JavaDoc ( https://docs.Oracle.com/javase/7/docs/api/Java/util/concurrent/CountDownLatch.html ) ، يعتبر CountDownLatch أداة مساعدة للمزامنة ، تم تقديمها في Java 5. هنا التزامن لا يعني تقييد الوصول إلى قسم حرج. ولكن تسلسل الإجراءات من المواضيع المختلفة. يشبه نوع التزامن الذي تم تحقيقه من خلال CountDownLatch نوع Join. افترض أن هناك خيطًا "M" يحتاج إلى انتظار مؤشرات ترابط العامل الأخرى "T1" و "T2" و "T3" لإكمال مهامه قبل Java 1.5 ، تكون الطريقة التي يمكن بها ذلك هي M تشغيل التعليمة البرمجية التالية

    T1.join();
    T2.join();
    T3.join();

تعمل التعليمة البرمجية أعلاه على التأكد من أن مؤشر الترابط M يستأنف عمله بعد انتهاء T1 و T2 و T3 من عمله. يمكن T1 و T2 و T3 إكمال أعمالهم بأي ترتيب. يمكن تحقيق نفس الشيء من خلال CountDownLatch ، حيث تشترك T1 و T2 و T3 و thread M في نفس كائن CountDownLatch.
طلبات "M": countDownLatch.await();
حيث أن "T1" و "T2" و "T3" تعمل countDownLatch.countdown();

عيب واحد مع طريقة الصلة هو أن M يجب أن يعرف عن T1 ، T2 ، T3. إذا كان هناك مؤشر ترابط عامل جديد T4 يضاف لاحقًا ، فيجب على M أن يكون مدركًا له أيضًا. يمكن تجنب ذلك مع CountDownLatch. بعد التنفيذ ، سيكون تسلسل الإجراء هو [T1 ، T2 ، T3] (ترتيب T1 ، T2 ، T3 يمكن أن يكون على أي حال) -> [M]

1
S R Chaitanya

أفضل مثال على الوقت الفعلي ل countDownLatch الموضح في هذا الرابط CountDownLatchExample

0
Ashwin Patil