неділя, 4 вересня 2011 р.

Java: CountDownLatch і мій камінь спотикання...

Спочатку я думав: зараз підійду академічно. Напишу основи, перейду до циклів, класи, методи і "понеслась"... Проте після того, як я двічі сходив в контору і мене загнобили цим CountDownLatch(ером), я вирішив прийти додому, відкрить Еккеля і прочитать - ну що ж воно це за звірюка така, що люди в мені не бачать Java Junior Developer без нього. Теоретичну частину ви зможете знайти і самі в Еккелі, або деінде, я ж розповім в парі десятках слів, що воно таке і як до цього дожились.
 Спочатку декілька вступних слів. В наш час, коли однопроцесорні ПК починають переходити в розряд музейних експонатів на сцені з"явилися "Ріал монстерс" потипу: "Гикач", "Обліна", "Нечупара", "Гримль" (2-х, 4-х, 6-и, 8-и) ядерні. 
Розробники Java давно вже граються з багатопоточністю. Якщо Ви хоч трохи її бачили, то точно знаєте про інтерфейс Runnable та  клас Threads. Проте, на місці ніхто не стоїть, і в Java SE5, з'явилася супер-пупер-мега бібліотека з назвою java.util.concurrent.*;
Значить, товариш Еккель каже: "CountDownLatch - клас синхронізує задачі і заставляє їх очікувати завершення групи операцій, що виконуються іншими задачами".
Об"єкту CountDownLatch надається початкове значення лічильника, а всі задачі, які викликають await(), доречі забігаючи на перед, нам цікаві два методи, цього "щастя": вище згаданий await() та нище описаний countDown(); Так от, якщо в методі є await() цього каунта, то він собі чекає, як вірний песик, поки інші методи не откаунтаудять до нуля і він зможе кинутись в бій, аби вибивати для себе місце під сонцем, тобто пару тактових ударів серця комп"ютера. Необхідно обов"язково  сказати, що даний клас є одноразовий, і може каунтданиться тільки в низ до нуля, тобто ні кроку назад, як шльопки. Якщо Вам треба щось, що може вальсувати вверх-вниз, то  Вам до CyclicBarrier, але це зовсім інша історія...Дуже важливо: задачі, які викликають в собі countDown(), не блокуються на час виклика. + один процес може не один раз каунтдаунить.
Приклад, відповідно, теж брався з усіма любимого і читаного.
Значить ідея, така:
Створюється "тьма" потоків, вони виконуються і кожент каунтить по разу до поки не стане нуль, після чого бідолаха, який сидів і чекав з await() - ом виривається і виконується. До речі, хто не чув про ExecutorService,  то треба знайти і прочитати, це теж щастя. яке привалило з Java SE5.




import java.util.Random;
import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class TaskPoriton implements Runnable{
    private static int counter = 0;
    private final int id = counter ++;
    private static Random rand = new Random(47);
    private final CountDownLatch latch;
    
    public TaskPoriton(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        
            doWork();
            latch.countDown();            
        
    }
    void doWork(){
        try {
            TimeUnit.SECONDS.sleep(rand.nextInt(20));
            System.out.println(this + " ended!!!");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }        
    }    
}
class WaitingTask implements   Runnable{
    private static int counter = 0;
    private final int id = counter ++;
    private static Random rand = new Random(47);
    private final CountDownLatch latch;
    
    public WaitingTask(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        //Як сказали б в "Простоквашино" - : "Вот это коварный тип", який чекає нуля.
            try {
                latch.await();
                TimeUnit.SECONDS.sleep(rand.nextInt(20));
                System.out.println("============="+this + "cross barier");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            }    
}
public class CountDownLatchUse {
    static final int SIZE = 100;
public static void main(String[] args) {
    ExecutorService exec = Executors.newCachedThreadPool();
    CountDownLatch latch = new CountDownLatch(SIZE);    
//Для наочності поміняно, спочатку створюються "паспжири", які чекають на маршрутку
    for (int i = 0; i < 10; i++)
        exec.execute(new WaitingTask(latch));А тут скажемо так, їдуть ті кого спочатку треба висадить, щоб могли зайти інші....     
    for (int i = 0; i < SIZE; i++)
        exec.execute(new TaskPoriton(latch));    
    System.out.println("All processes started -- >");    
    exec.shutdown();
}
}

Немає коментарів:

Дописати коментар