понедельник, 3 августа 2015 г.

Многопоточность Java

CountDownLatch и Semaphore

В общем то,достаточно сжатая информация об удобных вещах в пакете java.util.concurrent,
а точнее о паре вещей оттуда.

CountDownLatch

CountDownLatch класс защелка,позволяет задержать поток(и) до тех пор,пока не выполнится определенное условие,а именно пока счетчик заданный в конструкторе не дойдет до 0.
Вообще создание объекта происходит следующим образом


 
CountDownLatch latch = new CountDownLatch(3);//по количеству потоков которых мы будем ждать
И 2 основных метода  countDown и await.
Метод await заставляет текущий поток ожидать,пока счетчик защелки не станет равен 0.
Метод countDown декрементит счетчик заданный в конструкторе и как только он дойдет до 0,потоки продолжат выполнение.

Вот пример со скачками(пример не мой,я только напихал больше комментариев по коду,ну и сократил лишнее)


 
class Race
{
    private Random rand = new Random();
    private int distance = 50;
    private List horses = Arrays.asList("Vanek","Robert","Polson","Rak");
    public void run() throws InterruptedException
    {
        System.out.println("And the horses are stepping up to the gate...");
        final CountDownLatch start = new CountDownLatch(1);//защелка для одновременного старта для всех коней,1-тк защелка для основного потока выполнения
        start.await();
        final CountDownLatch finish = new CountDownLatch(horses.size());//защелка для финиша, а здесь по количеству лошадей
        final List places = Collections.synchronizedList(new ArrayList<>());
        horses.forEach(horse -> new Thread(() -> {
            try {
                System.out.println(horse + " stepping up to the gate...");
                start.await(); // Блокируем поток,чтобы не было не одного фальстарта у наших коней
                int traveled = 0;//пройденный путь для коня
                while (traveled < distance) {
                    // каждые 0-2 секунды лошадь проходит дистанцию 0-14 пунктов
                    Thread.sleep(rand.nextInt(3) * 1000);
                    traveled += rand.nextInt(15);
                    System.out.println(horse + " advanced to " + traveled + "!");
                }
                finish.countDown();//уменьшение счетчика защелки,тк мы будем ждать именно этого момента в самой программе и когда последний конь финиширует исполнение завершится
                System.out.println(horse + " crossed the finish!");
                places.add(horse);
            } catch (InterruptedException intEx) {
                System.out.println("ABORTING RACE!!!");
                intEx.printStackTrace();
            }
        }).start());
        System.out.println("And... they're off!");
        start.countDown();//снимаем нашу защелку для старта
        finish.await();//ожидаем финиша наших скакунов
        System.out.println("And we have our winners!");
        System.out.println(places.get(0) + " took the gold...");
        System.out.println(places.get(1) + " got the silver...");
        System.out.println("and " + places.get(2) + " took home the bronze.");
    }

    public static void main(String[] args) throws InterruptedException {
        new Race().run();
    }
}

Вот как то так.

Теперь про класс Semaphore.

Он, как видно из названия,служит для ограничения количества потоков использующих определенный ресурс.
Создается объект как то так

 
Semaphore available = new Semaphore(3);//по количеству потоков которые будут активны

И 2 основных метода acquire() и release().
acquire()-Уменьшает счетчик доступных потоков(который мы задали в конструкторе) на 1.
release()-Соответственно освобождает его и увеличивает счетчик.

Небольшой пример

 
 public static void main(String[] args)
    {
        Runnable limitedCall = new Runnable() {
            final Random rand = new Random();
            final Semaphore available = new Semaphore(3);
            int count = 0;
            public void run()
            {
                int time = rand.nextInt(15);
                int num = count++;
                try
                {
                    available.acquire();//задействуем поток
                    System.out.println("Executing " +
                            "long-running action for " +time +
                            " seconds... #" + num);

                    Thread.sleep(time * 1000);
                    System.out.println("Done with #" + num + "!");
                    available.release();//после его работы освобождаем 
                }
                catch (InterruptedException intEx)
                {
                    intEx.printStackTrace();
                }
            }
        };
        for (int i=0; i<10 data-blogger-escaped-i="" data-blogger-escaped-limitedcall="" data-blogger-escaped-new="" data-blogger-escaped-pre="" data-blogger-escaped-start="" data-blogger-escaped-thread="">
Создаем 10 потоков(можно убедиться выполнив команду jstack),однако только 3 из них активны.Как только один их потоков освобождает семафор,другой занимает его место.
Вот и все.