ΠŸΠ΅Ρ€Π΅Ρ‚ΡΠΆΠΊΠ°, ΠŸΡ€Π΅ΠΌΠΈΡ Π’ΠŸΡ€ΠΎΠ³Π΅Ρ€, 13.11
ΠŸΠ΅Ρ€Π΅Ρ‚ΡΠΆΠΊΠ°, ΠŸΡ€Π΅ΠΌΠΈΡ Π’ΠŸΡ€ΠΎΠ³Π΅Ρ€, 13.11
ΠŸΠ΅Ρ€Π΅Ρ‚ΡΠΆΠΊΠ°, ΠŸΡ€Π΅ΠΌΠΈΡ Π’ΠŸΡ€ΠΎΠ³Π΅Ρ€, 13.11

ΠœΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΠ΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π² Java 8. Π§Π°ΡΡ‚ΡŒ Ρ‚Ρ€Π΅Ρ‚ΡŒΡ. АтомарныС ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ ΠΈ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½Ρ‹Π΅ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹

ОблоТка: ΠœΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΠ΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π² Java 8. Π§Π°ΡΡ‚ΡŒ Ρ‚Ρ€Π΅Ρ‚ΡŒΡ. АтомарныС ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ ΠΈ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½Ρ‹Π΅ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹

РассказываСт Π‘Π΅Π½Π΄ΠΆΠ°ΠΌΠΈΠ½ Π’ΠΈΠ½Ρ‚Π΅Ρ€Π±Π΅Ρ€Π³, Software Engineer 

Π”ΠΎΠ±Ρ€ΠΎ ΠΏΠΎΠΆΠ°Π»ΠΎΠ²Π°Ρ‚ΡŒ Π² Ρ‚Ρ€Π΅Ρ‚ΡŒΡŽ Ρ‡Π°ΡΡ‚ΡŒ руководства ΠΏΠΎ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎΠΌΡƒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ Π² Java 8. Π’ ΠΏΠ΅Ρ€Π²ΠΎΠΉ части ΠΌΡ‹ рассматривали, ΠΊΠ°ΠΊ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ ΠΊΠΎΠ΄ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ², Π·Π°Π΄Π°Ρ‡ ΠΈ сСрвисов исполнитСлСй. Π’ΠΎ Π²Ρ‚ΠΎΡ€ΠΎΠΉ Ρ€Π°Π·Π±ΠΈΡ€Π°Π»ΠΈΡΡŒ с Ρ‚Π΅ΠΌ, ΠΊΠ°ΠΊ ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ доступ ΠΊ измСняСмым ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠ»ΡŽΡ‡Π΅Π²ΠΎΠ³ΠΎ слова synchronized, Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ ΠΈ сСмафоров. БСгодня, Π² Π·Π°ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ части, я расскаТу ΠΎ Π΄Π²ΡƒΡ… ΠΎΡ‡Π΅Π½ΡŒ Π²Π°ΠΆΠ½Ρ‹Ρ… частях Concurrency API: ΠΎΠ± Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½Ρ‹Ρ… ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… ΠΈ ΠΎ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½Ρ‹Ρ… Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ… (Concurrent Maps).

AtomicInteger

ΠŸΠ°ΠΊΠ΅Ρ‚ java.concurrent.atomic содСрТит ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹Ρ… классов для выполнСния Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½Ρ‹Ρ… ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ. ΠžΠΏΠ΅Ρ€Π°Ρ†ΠΈΡ называСтся Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½ΠΎΠΉ Ρ‚ΠΎΠ³Π΄Π°, ΠΊΠΎΠ³Π΄Π° Π΅Ρ‘ ΠΌΠΎΠΆΠ½ΠΎ бСзопасно Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ ΠΏΡ€ΠΈ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½Ρ‹Ρ… вычислСниях Π² Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΠΏΠΎΡ‚ΠΎΠΊΠ°Ρ…, Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΏΡ€ΠΈ этом Π½ΠΈ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ, Π½ΠΈ synchronized, ΠΊΠ°ΠΊ ΠΌΡ‹ это Π΄Π΅Π»Π°Π»ΠΈ Π² ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ ΡƒΡ€ΠΎΠΊΠ΅.

Π’Π½ΡƒΡ‚Ρ€ΠΈ Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½Ρ‹Π΅ классы ΠΎΡ‡Π΅Π½ΡŒ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ сравнСниС с ΠΎΠ±ΠΌΠ΅Π½ΠΎΠΌ (compare-and-swap, CAS), Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½ΡƒΡŽ ΠΈΠ½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡŽ, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²ΠΎ соврСмСнных процСссоров. Π­Ρ‚ΠΈ инструкции Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ Π³ΠΎΡ€Π°Π·Π΄ΠΎ быстрСС, Ρ‡Π΅ΠΌ синхронизация с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ, Ссли Π²Π°ΠΌ просто Π½ΡƒΠΆΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΎΠ΄Π½Ρƒ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ², Π»ΡƒΡ‡ΡˆΠ΅ Π²Ρ‹Π±ΠΈΡ€Π°Ρ‚ΡŒ Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½Ρ‹Π΅ классы.

ΠŸΡ€ΠΈΠ²Π΅Π΄Ρƒ нСсколько ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ² с использованиСм AtomicInteger, ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈΠ· Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½Ρ‹Ρ… классов:

			AtomicInteger atomicInt = new AtomicInteger(0);

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 1000)
    .forEach(i -> executor.submit(atomicInt::incrementAndGet));

stop(executor);

System.out.println(atomicInt.get());    // => 1000
		

Как Π²ΠΈΠ΄ΠΈΡ‚Π΅, использованиС AtomicInteger вмСсто ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠ³ΠΎ Integer ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΠ»ΠΎ Π½Π°ΠΌ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΡ‚ΡŒ число, распрСдСлив Ρ€Π°Π±ΠΎΡ‚Ρƒ сразу ΠΏΠΎ Π΄Π²ΡƒΠΌ ΠΏΠΎΡ‚ΠΎΠΊΠ°ΠΌ. ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π½Π΅ Π±Π΅ΡΠΏΠΎΠΊΠΎΠΈΡ‚ΡŒΡΡ ΠΎ бСзопасности, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ incrementAndGet() являСтся Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½ΠΎΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ.

Класс AtomicInteger ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ ΠΌΠ½ΠΎΠ³ΠΎ Ρ€Π°Π·Π½Ρ‹Ρ… Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½Ρ‹Ρ… ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ. ΠœΠ΅Ρ‚ΠΎΠ΄ updateAndGet() ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Π² качСствС Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π° лямбда-Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΈ выполняСт Π½Π°Π΄ числом Π·Π°Π΄Π°Π½Π½Ρ‹Π΅ арифмСтичСскиС ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ:

			AtomicInteger atomicInt = new AtomicInteger(0);

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 1000)
    .forEach(i -> {
        Runnable task = () ->
            atomicInt.updateAndGet(n -> n + 2);
        executor.submit(task);
    });

stop(executor);

System.out.println(atomicInt.get());    // => 2000
		

ΠœΠ΅Ρ‚ΠΎΠ΄ accumulateAndGet() ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ лямбда-выраТСния Ρ‚ΠΈΠΏΠ° IntBinaryOperator. Π’ΠΎΡ‚ ΠΊΠ°ΠΊ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΎΡΡƒΠΌΠΌΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ всС числа ΠΎΡ‚ нуля Π΄ΠΎ тысячи:

			AtomicInteger atomicInt = new AtomicInteger(0);

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 1000)
    .forEach(i -> {
        Runnable task = () ->
            atomicInt.accumulateAndGet(i, (n, m) -> n + m);
        executor.submit(task);
    });

stop(executor);

System.out.println(atomicInt.get());    // => 499500
		

Π‘Ρ€Π΅Π΄ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΡ… Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½Ρ‹Ρ… классов хочСтся ΡƒΠΏΠΎΠΌΡΠ½ΡƒΡ‚ΡŒ Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ AtomicBoolean, AtomicLong ΠΈ AtomicReference.

LongAdder

Класс LongAdder ΠΌΠΎΠΆΠ΅Ρ‚ Π²Ρ‹ΡΡ‚ΡƒΠΏΠ°Ρ‚ΡŒ Π² качСствС Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Ρ‹ AtomicLong для ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ слоТСния чисСл.

			ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 1000)
    .forEach(i -> executor.submit(adder::increment));

stop(executor);

System.out.println(adder.sumThenReset());   // => 1000
		

Π’Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ Ρƒ Π΄Ρ€ΡƒΠ³ΠΈΡ… Π°Ρ‚ΠΎΠΌΠ°Ρ€Π½Ρ‹Ρ… чисСл, Ρƒ LongAdder Π΅ΡΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ increment() ΠΈ add(). Но вмСсто Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΊΠ»Π°Π΄Ρ‹Π²Π°Ρ‚ΡŒ числа сразу, ΠΎΠ½ просто Ρ…Ρ€Π°Π½ΠΈΡ‚ Ρƒ сСбя Π½Π°Π±ΠΎΡ€ слагаСмых, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΡ‚ΡŒ взаимодСйствиС ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΠΎΡ‚ΠΎΠΊΠ°ΠΌΠΈ. Π£Π·Π½Π°Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΌΠΎΠΆΠ½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π²Ρ‹Π·ΠΎΠ²Π° sum() ΠΈΠ»ΠΈ sumThenReset(). Π­Ρ‚ΠΎΡ‚ класс ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² ситуациях, ΠΊΠΎΠ³Π΄Π° Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ числа приходится Π³ΠΎΡ€Π°Π·Π΄ΠΎ Ρ‡Π°Ρ‰Π΅, Ρ‡Π΅ΠΌ Π·Π°ΠΏΡ€Π°ΡˆΠΈΠ²Π°Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ (часто это ΠΊΠ°ΠΊΠΈΠ΅-Ρ‚ΠΎ статистичСскиС исслСдованиС, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ подсчёт количСства запросов). НСслоТно Π΄ΠΎΠ³Π°Π΄Π°Ρ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ, давая прирост Π² ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, LongAdder Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ Π³ΠΎΡ€Π°Π·Π΄ΠΎ большСго количСства памяти ΠΈΠ·-Π·Π° Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ ΠΎΠ½ Ρ…Ρ€Π°Π½ΠΈΡ‚ всС слагаСмыС.

LongAccumulator

Класс LongAccumulator нСсколько Ρ€Π°ΡΡˆΠΈΡ€ΡΠ΅Ρ‚ возмоТности LongAdder. ВмСсто простого слоТСния ΠΎΠ½ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅Ρ‚ входящиС значСния с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ лямбды Ρ‚ΠΈΠΏΠ° LongBinaryOperator, которая пСрСдаётся ΠΏΡ€ΠΈ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ. Выглядит это Ρ‚Π°ΠΊ:

			LongBinaryOperator op = (x, y) -> 2 * x + y;
LongAccumulator accumulator = new LongAccumulator(op, 1L);

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 10)
    .forEach(i -> executor.submit(() -> accumulator.accumulate(i)));

stop(executor);

System.out.println(accumulator.getThenReset());     // => 2539
		

Π’ этом ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ Π²Ρ‹Π·ΠΎΠ²Π΅ accumulate() Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ аккумулятора увСличиваСтся Π² Π΄Π²Π° Ρ€Π°Π·Π°, ΠΈ лишь Π·Π°Ρ‚Π΅ΠΌ суммируСтся с i. Π’Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ LongAdder, LongAccumulator Ρ…Ρ€Π°Π½ΠΈΡ‚ вСсь Π½Π°Π±ΠΎΡ€ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½Ρ‹Ρ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ Π² памяти.


ΠΏΡ€ΠΈΠΌ. ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄Ρ‡ΠΈΠΊΠ° На самом Π΄Π΅Π»Π΅, ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π½Π΅ совсСм ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹ΠΉ; согласно Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ, LongAccumulator Π½Π΅ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ порядка выполнСния ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ. ΠšΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠΉ Ρ„ΠΎΡ€ΠΌΡƒΠ»ΠΎΠΉ Π±Ρ‹Π»Π° Π±Ρ‹, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ x+2*y, Ρ‚.ΠΊ. ΠΏΡ€ΠΈ любом порядкС выполнСния Π² ΠΊΠΎΠ½Ρ†Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒΡΡ ΠΎΠ΄Π½ΠΎ ΠΈ Ρ‚ΠΎ ΠΆΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.

ConcurrentMap

Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ ConcurrentMap наслСдуСтся ΠΎΡ‚ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠ³ΠΎ Map ΠΈ прСдоставляСт описаниС ΠΎΠ΄Π½ΠΎΠΉ ΠΈΠ· самой ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠΉ ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ для ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½ΠΎΠ³ΠΎ использования. Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΎΠ΄Π΅ΠΌΠΎΠ½ΡΡ‚Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ интСрфСйса, ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π²ΠΎΡ‚ эту Π·Π°Π³ΠΎΡ‚ΠΎΠ²ΠΊΡƒ:

			ConcurrentMap map = new ConcurrentHashMap<>();
map.put("foo", "bar");
map.put("han", "solo");
map.put("r2", "d2");
map.put("c3", "p0");
		

ΠœΠ΅Ρ‚ΠΎΠ΄ forEach() ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ лямбду Ρ‚ΠΈΠΏΠ° BiConsumer. Π­Ρ‚ΠΎΠΉ лямбдС Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Ρ‚ΡŒΡΡ Π² качСствС Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ² всС ΠΊΠ»ΡŽΡ‡ΠΈ ΠΈ значСния Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ ΠΏΠΎ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ. Π­Ρ‚ΠΎΡ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΊΠ°ΠΊ Π·Π°ΠΌΠ΅Π½Π° for-each Ρ†ΠΈΠΊΠ»Π°ΠΌ с ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ ΠΏΠΎ всСм Entry. Π˜Ρ‚Π΅Ρ€Π°Ρ†ΠΈΡ выполняСтся ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ, Π² Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΌ ΠΏΠΎΡ‚ΠΎΠΊΠ΅. эту Π·Π°ΠΏΡΡ‚ΡƒΡŽ Π½Π΅ Π½Π°Π΄ΠΎ ΡƒΠ±ΠΈΡ€Π°Ρ‚ΡŒ. Ссли Π²Ρ‹ считаСтС, Ρ‡Ρ‚ΠΎ Π½Π°Π΄ΠΎ - поТалуйста, сначала скаТитС ΠΌΠ½Π΅

			map.forEach((key, value) -> System.out.printf("%s = %s\n", key, value));
		

ΠœΠ΅Ρ‚ΠΎΠ΄ putIfAbsent() ΠΏΠΎΠΌΠ΅Ρ‰Π°Π΅Ρ‚ Π² Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ссли ΠΏΠΎ Π΄Π°Π½Π½ΠΎΠΌΡƒ ΠΊΠ»ΡŽΡ‡Ρƒ Π΅Ρ‰Ρ‘ Π½Π΅Ρ‚ Π΄Ρ€ΡƒΠ³ΠΎΠ³ΠΎ значСния. Π­Ρ‚ΠΎΡ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄ являСтся потокобСзопасным (ΠΎ ΠΊΡ€Π°ΠΉΠ½Π΅ΠΉ ΠΌΠ΅Ρ€Π΅, Π² Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ConcurrentHashMap), поэтому Π²Π°ΠΌ Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ synchronized, ΠΊΠΎΠ³Π΄Π° Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π³ΠΎ Π² Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΠΏΠΎΡ‚ΠΎΠΊΠ°Ρ… (Ρ‚ΠΎ ΠΆΠ΅ самоС справСдливо ΠΈ для ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠ³ΠΎ put()):

			String value = map.putIfAbsent("c3", "p1");
System.out.println(value);    // p0
		

ΠœΠ΅Ρ‚ΠΎΠ΄ getOrDefault() Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΉ get(), с Ρ‚ΠΎΠΉ лишь Ρ€Π°Π·Π½ΠΈΡ†Π΅ΠΉ, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΈ отсутствии значСния ΠΏΠΎ Π΄Π°Π½Π½ΠΎΠΌΡƒ ΠΊΠ»ΡŽΡ‡Ρƒ ΠΎΠ½ Π²Π΅Ρ€Π½Ρ‘Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ-ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ, ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Π΅ΠΌΠΎΠ΅ Π²Ρ‚ΠΎΡ€Ρ‹ΠΌ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ:

			String value = map.getOrDefault("hi", "there");
System.out.println(value);    // there
		

ΠœΠ΅Ρ‚ΠΎΠ΄ replaceAll() ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Π² качСствС Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π° лямбда-Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Ρ‚ΠΈΠΏΠ° BiFunction. Π­Ρ‚ΠΎΠΉ лямбдС ΠΏΠΎ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ всС ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΠΈ ΠΊΠ»ΡŽΡ‡-значСния ΠΈΠ· ΠΊΠ°Ρ€Ρ‚Ρ‹, Π° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΎΠ½Π° Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚, записываСтся ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰Π΅ΠΌΡƒ ΠΊΠ»ΡŽΡ‡Ρƒ Π² качСствС значСния:

			map.replaceAll((key, value) -> "r2".equals(key) ? "d3" : value);
System.out.println(map.get("r2"));    // d3
		

Если ΠΆΠ΅ Π²Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Ρ‚Π°ΠΊΠΈΠΌ ΠΆΠ΅ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ΄ΠΈΠ½ ΠΊΠ»ΡŽΡ‡, это позволяСт ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄ compute():

			map.compute("foo", (key, value) -> value + value);
System.out.println(map.get("foo"));   // barbar
		

ΠšΡ€ΠΎΠΌΠ΅ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠ³ΠΎ compute(), ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‚ Ρ‚Π°ΠΊ ΠΆΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ computeIfAbsent() ΠΈ computeIfPresent(). Они ΠΈΠ·ΠΌΠ΅Π½ΡΡŽΡ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ссли Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ Π΄Π°Π½Π½ΠΎΠΌΡƒ ΠΊΠ»ΡŽΡ‡Ρƒ отсутствуСт (ΠΈΠ»ΠΈ присутствуСт, соотвСтствСнно).

И, Π½Π°ΠΊΠΎΠ½Π΅Ρ†, ΠΌΠ΅Ρ‚ΠΎΠ΄ merge(), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ использован для объСдинСния ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ ΠΊΠ»ΡŽΡ‡Π° с Π½ΠΎΠ²Ρ‹ΠΌ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ. Π’ качСствС Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π° ΠΎΠ½ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ ΠΊΠ»ΡŽΡ‡, Π½ΠΎΠ²ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΈ лямбду, которая опрСдСляСт, ΠΊΠ°ΠΊ Π½ΠΎΠ²ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ объСдинСно со старым:

			map.merge("foo", "boo", (oldVal, newVal) -> newVal + " was " + oldVal);
System.out.println(map.get("foo"));   // boo was bar
		

ConcurrentHashMap

ΠšΡ€ΠΎΠΌΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ описаны Π² ConcurrencyMap, Π² ConcurrentHashMap Π±Ρ‹Π»ΠΎ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΎ ΠΈ Π΅Ρ‰Ρ‘ нСсколько своих. Π’Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½Ρ‹Π΅ stream’ы, эти ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ ForkJoinPool, доступный Ρ‡Π΅Ρ€Π΅Π· ForkJoinPool.commonPool() Π² Java 8. Π­Ρ‚ΠΎΡ‚ ΠΏΡƒΠ» ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ свои настройки для количСства ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ², основанныС Π½Π° количСствС ядСр. Π£ мСня ΠΈΡ… 4, Π° Π·Π½Π°Ρ‡ΠΈΡ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π±ΡƒΠ΄Π΅Ρ‚ Ρ‚Ρ€ΠΈ ΠΏΠΎΡ‚ΠΎΠΊΠ°:

			System.out.println(ForkJoinPool.getCommonPoolParallelism());  // 3
		

Π­Ρ‚ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° JVM:

			-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
		

ΠœΡ‹ рассмотрим Ρ‚Ρ€ΠΈ Π½ΠΎΠ²Ρ‹Ρ… ΠΌΠ΅Ρ‚ΠΎΠ΄Π°: forEach, search and reduce. Π£ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΈΠ· Π½ΠΈΡ… Π΅ΡΡ‚ΡŒ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ называСтся parallelismThreshold, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ опрСдСляСт минимальноС количСство элСмСнтов Π² ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ, ΠΏΡ€ΠΈ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ опСрация Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ Π² Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΠΏΠΎΡ‚ΠΎΠΊΠ°Ρ…. Π’.Π΅. Ссли Π² ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ 499 элСмСнтов, Π° ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ выставлСн Ρ€Π°Π²Π½Ρ‹ΠΌ пятистам, Ρ‚ΠΎ опСрация Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ Π² ΠΎΠ΄Π½ΠΎΠΌ ΠΏΠΎΡ‚ΠΎΠΊΠ΅ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ. Π’ Π½Π°ΡˆΠΈΡ… ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°Ρ… ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ Ρ€Π°Π²Π½Ρ‹ΠΌ Π² Π΅Π΄ΠΈΠ½ΠΈΡ†Π΅, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ всСгда Π²Ρ‹ΠΏΠΎΠ»Π½ΡΠ»ΠΈΡΡŒ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎ.

Для ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ² Π½ΠΈΠΆΠ΅ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ всё Ρ‚Ρƒ ΠΆΠ΅ Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ, Ρ‡Ρ‚ΠΎ ΠΈ Π²Ρ‹ΡˆΠ΅ (ΠΎΠ΄Π½Π°ΠΊΠΎ объявим Π΅Ρ‘ ΠΈΠΌΠ΅Π½Π΅ΠΌ класса, Π° Π½Π΅ интСрфСйса. Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π°ΠΌ Π±Ρ‹Π»ΠΈ доступны всС ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹):

			ConcurrentHashMap map = new ConcurrentHashMap<>();
map.put("foo", "bar");
map.put("han", "solo");
map.put("r2", "d2");
map.put("c3", "p0");
		

ForEach

Π Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄ Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ Π² ConcurrentMap. Для ΠΈΠ»Π»ΡŽΡΡ‚Ρ€Π°Ρ†ΠΈΠΈ многопоточности ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Π²Ρ‹Π²ΠΎΠ΄ΠΈΡ‚ΡŒ названия ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² (Π½Π΅ Π·Π°Π±Ρ‹Π²Π°ΠΉΡ‚Π΅, Ρ‡Ρ‚ΠΎ ΠΈΡ… количСство для мСня ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΎ трСмя):

			map.forEach(1, (key, value) ->
    System.out.printf("key: %s; value: %s; thread: %s\n",
        key, value, Thread.currentThread().getName()));

// key: r2; value: d2; thread: main
// key: foo; value: bar; thread: ForkJoinPool.commonPool-worker-1
// key: han; value: solo; thread: ForkJoinPool.commonPool-worker-2
// key: c3; value: p0; thread: main
		

Search

ΠœΠ΅Ρ‚ΠΎΠ΄ search() ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ лямбда-Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Ρ‚ΠΈΠΏΠ° BiFunction, Π² ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ всС ΠΏΠ°Ρ€Ρ‹ ΠΊΠ»ΡŽΡ‡-Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ. Ѐункция Π΄ΠΎΠ»ΠΆΠ½Π° Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ null, Ссли Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΠ΅ Π²Ρ…ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ. Π’ случаС, Ссли функция Π²Π΅Ρ€Π½Ρ‘Ρ‚ Π½Π΅ null, дальнСйший поиск Π±ΡƒΠ΄Π΅Ρ‚ остановлСн. НС Π·Π°Π±Ρ‹Π²Π°ΠΉΡ‚Π΅, Ρ‡Ρ‚ΠΎ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ…ΡΡˆ-Ρ‚Π°Π±Π»ΠΈΡ†Π΅ хранятся нСупорядочСнно. Если Π²Ρ‹ Π±ΡƒΠ΄Π΅Ρ‚Π΅ ΠΏΠΎΠ»Π°Π³Π°Ρ‚ΡŒΡΡ Π½Π° порядок, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π²Ρ‹ добавляли Π΄Π°Π½Π½Ρ‹Π΅ Π² Π½Π΅Ρ‘, Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌΠΎΠ³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°. Если условиям поиска ΡƒΠ΄ΠΎΠ²Π»Π΅Ρ‚Π²ΠΎΡ€ΡΡŽΡ‚ нСсколько Π²Ρ…ΠΎΠΆΠ΄Π΅Π½ΠΈΠΉ, Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Ρ‚ΠΎΡ‡Π½ΠΎ ΠΏΡ€Π΅Π΄ΡΠΊΠ°Π·Π°Ρ‚ΡŒ нСльзя.

			String result = map.search(1, (key, value) -> {
    System.out.println(Thread.currentThread().getName());
    if ("foo".equals(key)) {
        return value;
    }
    return null;
});
System.out.println("Result: " + result);

// ForkJoinPool.commonPool-worker-2
// main
// ForkJoinPool.commonPool-worker-3
// Result: bar
		

Или Π²ΠΎΡ‚ Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ полагаСтся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π½Π° значСния:

			String result = map.searchValues(1, value -> {
    System.out.println(Thread.currentThread().getName());
    if (value.length() > 3) {
        return value;
    }
    return null;
});

System.out.println("Result: " + result);

// ForkJoinPool.commonPool-worker-2
// main
// main
// ForkJoinPool.commonPool-worker-1
// Result: solo
		

Reduce

ΠœΠ΅Ρ‚ΠΎΠ΄ reduce() Π²Ρ‹ ΠΌΠΎΠ³Π»ΠΈ ΡƒΠΆΠ΅ Π²ΡΡ‚Ρ€Π΅Ρ‡Π°Ρ‚ΡŒ Π² Java 8 Streams. Он ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Π΄Π²Π΅ лямбды Ρ‚ΠΈΠΏΠ° BiFunction. ΠŸΠ΅Ρ€Π²Π°Ρ функция ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Ρ‹Π²Π°Π΅Ρ‚ ΠΏΠ°Ρ€Ρƒ ΠΊΠ»ΡŽΡ‡/Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π² ΠΎΠ΄ΠΈΠ½ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ (любого Ρ‚ΠΈΠΏΠ°). Вторая функция совмСщаСт всС ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Π΅ значСния Π² Π΅Π΄ΠΈΠ½Ρ‹ΠΉ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚, игнорируя Π»ΡŽΠ±Ρ‹Π΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ null-значСния.

			tring result = map.reduce(1,
    (key, value) -> {
        System.out.println("Transform: " + Thread.currentThread().getName());
        return key + "=" + value;
    },
    (s1, s2) -> {
        System.out.println("Reduce: " + Thread.currentThread().getName());
        return s1 + ", " + s2;
    });

System.out.println("Result: " + result);

// Transform: ForkJoinPool.commonPool-worker-2
// Transform: main
// Transform: ForkJoinPool.commonPool-worker-3
// Reduce: ForkJoinPool.commonPool-worker-3
// Transform: main
// Reduce: main
// Reduce: main
// Result: r2=d2, c3=p0, han=solo, foo=bar
		

На этом всё. НадСюсь, ΠΌΠΎΠΈ ΡΡ‚Π°Ρ‚ΡŒΠΈ Π±Ρ‹Π»ΠΈ Π²Π°ΠΌ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ ?

Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΠ΅ΠΌ