От java threads к лямбдам, Андрей Родионов
DESCRIPTION
Один из основных мотивов добавления в Java 8 лямбда-выражений — упростить написание многопоточных программ. На примере несложной вычислительной задачи я покажу эволюцию средств Java для многопоточности. Начнём с Java Threads, а закончим лямбда-выражениями и Stream API. Ну и в результате посмотрим, что и как вышло.TRANSCRIPT
![Page 2: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/2.jpg)
The Green Project
![Page 3: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/3.jpg)
The Star7 PDA • SPARC based, handheld wireless PDA • with a 5" color LCD with touchscreen input • a new 16 bit color hardware double buffered NTSC framebuffer • 900MHz wireless networking • multi-media audio codec
• a new power supply/battery interface • a version of Unix (SolarisOs) that runs in under a megabyte including drivers for PCMCIA • radio networking • flash RAM file system
![Page 4: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/4.jpg)
+ Оак • a new small, safe, secure, distributed, robust,
interpreted, garbage collected, multi-threaded, architecture neutral, high performance, dynamic programming language
• a set of classes that implement a spatial user interface metaphor, a user interface methodology which uses animation, audio, spatial cues, gestures
• All of this, in 1992!
![Page 5: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/5.jpg)
Зачем это все?
• Если Oak предназначался для подобных устройств, когда еще было не особо много многопроцессорных машин (и тем более никто не мечтала о телефоне с 4 ядрами), то зачем он изначально содержал поддержку потоков???
![Page 6: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/6.jpg)
![Page 7: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/7.jpg)
Напишем реализации одной и той же задачи с использованием
• Sequential algorithm
• Java Threads
• java.util.concurrent (Thread pool)
• Fork/Join
• Java 8 Stream API (Lambda)
![Page 8: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/8.jpg)
А так же …
• Сравним производительность каждого из подходов
![Page 9: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/9.jpg)
MicroBenchmarking?!
![Page 10: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/10.jpg)
Вы занимаетесь микробенчмаркингом? Тогда мы идем к Вам!
(The Art Of) (Java) Benchmarking http://shipilev.net/
![Page 11: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/11.jpg)
http://openjdk.java.net/projects/code-tools/jmh/ JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targetting the JVM.
![Page 12: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/12.jpg)
В качестве задачи – численное интегрирование
• Методом прямоугольников
![Page 13: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/13.jpg)
Sequential algorithm
![Page 14: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/14.jpg)
Sequential v.1 public class SequentialCalculate {
public double calculate(double start, double end,
double step) {
double result = 0.0;
double x = start;
while (x < end) {
result +=
step * (sin(x) * sin(x) + cos(x) * cos(x));
x += step;
}
return result;
}
}
![Page 15: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/15.jpg)
Sequential v.1 public class SequentialCalculate {
public double calculate(double start, double end,
double step) {
double result = 0.0;
double x = start;
while (x < end) {
result +=
step * (sin(x) * sin(x) + cos(x) * cos(x));
x += step;
}
return result;
}
}
![Page 16: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/16.jpg)
Sequential v.2: with Functional interface public interface Function<T, R> {
R apply(T t);
}
![Page 17: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/17.jpg)
Sequential v.2: with Functional interface public interface Function<T, R> {
R apply(T t);
}
public class SequentialCalculate {
private final Function<Double, Double> func;
public SequentialCalculate(Function<Double, Double> func) {
this.func = func;
}
public double calculate(double start, double end,
double step) {
double result = 0.0;
double x = start;
while (x < end) {
result += step * func.apply(x);
x += step;
}
return result;
}
}
![Page 18: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/18.jpg)
Sequential v.2 With Functional interface
SequentialCalculate sc = new SequentialCalculate (
new Function<Double, Double>() {
public Double apply(Double x) {
return sin(x) * sin(x) + cos(x) * cos(x);
}
} );
![Page 19: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/19.jpg)
Performance
• Intel Core i7-4770, 3.4GHz, 4 Physical cores + 4 Hyper threading = 8 CPUs
• Sun UltraSPARC T1, 1.0GHz, 8 Physical cores * 4 Light Weight Processes = 32 CPUs
54,832
1,877
0 10 20 30 40 50 60
t, sec
![Page 20: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/20.jpg)
Java Threads
![Page 21: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/21.jpg)
Как будем параллелить?
Thread 1 Thread 2 Thread N
![Page 22: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/22.jpg)
class CalcThread extends Thread {
private final double start;
private final double end;
private final double step;
private double partialResult;
public CalcThread(double start, double end,
double step) {
this.start = start;
this.end = end;
this.step = step;
}
public void run() {
double x = start;
while (x < end) {
partialResult += step * func.apply(x);
x += step;
}
}
}
![Page 23: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/23.jpg)
public double calculate(double start, double end,
double step, int chunks) {
CalcThread[] calcThreads = new CalcThread[chunks];
double interval = (end - start) / chunks;
double st = start;
for (int i = 0; i < chunks; i++) {
calcThreads[i] =
new CalcThread(st, st + interval, step);
calcThreads[i].start();
st += interval;
}
double result = 0.0;
for (CalcThread cs : calcThreads) {
cs.join();
result += cs.partialResult;
}
return result;
}
![Page 24: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/24.jpg)
Spliterator
Collector
public double calculate(double start, double end,
double step, int chunks) {
CalcThread[] calcThreads = new CalcThread[chunks];
double interval = (end - start) / chunks;
double st = start;
for (int i = 0; i < chunks; i++) {
calcThreads[i] =
new CalcThread(st, st + interval, step);
calcThreads[i].start();
st += interval;
}
double result = 0.0;
for (CalcThread cs : calcThreads) {
cs.join();
result += cs.partialResult;
}
return result;
}
![Page 25: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/25.jpg)
0
0,5
1
1,5
2
1 2 4 8 16 32
t (s
ec)
Threads
Execution time
Simple Threads
![Page 26: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/26.jpg)
0
1
2
3
4
5
6
2 4 8 16 32
Spe
ed
up
Threads
Speedup
Simple Threads
![Page 27: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/27.jpg)
Ограничения классического подхода
• "поток-на-задачу" хорошо работает с небольшим количеством долгосрочных задач
• слияние низкоуровневого кода, отвечающего за многопоточное исполнение, и высокоуровневого кода, отвечающего за основную функциональность приложения приводит к т.н. «спагетти-коду»
• трудности связанные с управлением потоками • поток занимает относительно много места в
памяти ~ 1 Mb • для выполнения новой задачи потребуется
запустить новый поток – это одна из самых требовательных к ресурсам операций
![Page 28: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/28.jpg)
java.util.concurrent
![Page 29: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/29.jpg)
Thread pool • Пул потоков - это очередь в сочетании с
фиксированной группой рабочих потоков, в которой используются wait() и notify(), чтобы сигнализировать ожидающим потокам, что прибыла новая работа.
![Page 30: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/30.jpg)
class CalcThread implements Callable<Double> {
private final double start;
private final double end;
private final double step;
public CalcThread(double start, double end, double step){
this.start = start;
this.end = end;
this.step = step;
}
public Double call() {
double partialResult = 0.0;
double x = start;
while (x < end) {
partialResult += step * func.apply(x);
x += step;
}
return partialResult;
}
}
![Page 31: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/31.jpg)
public double calculate(double start, double end,
double step, int chunks) {
ExecutorService executorService =
Executors.newFixedThreadPool(chunks);
Future<Double>[] futures = new Future[chunks];
double interval = (end - start) / chunks;
double st = start;
for (int i = 0; i < chunks; i++) {
futures[i] = executorService.submit(
new CalcThread(st, st + interval, step));
st += interval;
}
executorService.shutdown();
double result = 0.0;
for (Future<Double> partRes : futures) {
result += partRes.get();
}
return result;
}
![Page 32: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/32.jpg)
public double calculate(double start, double end,
double step, int chunks) {
ExecutorService executorService =
Executors.newFixedThreadPool(chunks);
Future<Double>[] futures = new Future[chunks];
double interval = (end - start) / chunks;
double st = start;
for (int i = 0; i < chunks; i++) {
futures[i] = executorService.submit(
new CalcThread(st, st + interval, step));
st += interval;
}
executorService.shutdown();
double result = 0.0;
for (Future<Double> partRes : futures) {
result += partRes.get();
}
return result;
}
Spliterator
Collector
![Page 33: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/33.jpg)
0
0,5
1
1,5
2
1 2 4 8 16 32
t (s
ec)
Threads
Execution time
Simple Threads
Thread Pool
![Page 34: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/34.jpg)
0
1
2
3
4
5
6
2 4 8 16 32
Spe
ed
up
Threads
Speedup
Simple Threads Thread Pool
![Page 35: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/35.jpg)
«Бытие определяет сознание» Доминирующие в текущий момент аппаратные
платформы формируют подход к созданию языков, библиотек и систем
• С самого момента зарождения языка в Java была поддержка потоков и параллелизма (Thread, synchronized, volatile, …)
• Однако примитивы параллелизма, введенные в 1995 году, отражали реальность аппаратного обеспечения того времени: большинство доступных коммерческих систем вообще не предоставляли возможностей использования параллелизма, и даже наиболее дорогостоящие системы предоставляли такие возможности лишь в ограниченных масштабах
• В те дни потоки использовались в основном, для выражения asynchrony, а не concurrency, и в результате, эти механизмы в целом отвечали требованиям времени
![Page 36: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/36.jpg)
Путь к параллелизму • По мере изменения доминирующей аппаратной платформы,
должна соответственно изменяться и программная платформа • Когда начался процесс удешевления многопроцессорных систем,
от приложений стали требовать все большего использования предоставляемого системами аппаратного параллелизма. Тогда программисты обнаружили, что разрабатывать параллельные программы, использующие низкоуровневые примитивы, обеспечиваемые языком и библиотекой классов, сложно и чревато ошибками
• java.util.concurrent дала возможности для «coarse-grained» параллелизма (поток на запрос), но этого может быть не достаточно, т.к. сам по себе запрос может выполняться долго
• Необходимы средства для «finer-grained» параллелизма
Web server
Th1 Th2 Th3 ThN coarse-grained parallelism
finer-grained parallelism
![Page 37: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/37.jpg)
Fork/Join
![Page 38: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/38.jpg)
Fork/Join
// PSEUDOCODE
Result solve(Problem problem) {
if (problem.size < SEQUENTIAL_THRESHOLD)
return solveSequentially(problem);
else {
Result left, right;
INVOKE-IN-PARALLEL {
left = solve(extractLeftHalf(problem));
right = solve(extractRightHalf(problem));
}
return combine(left, right);
}
}
• Fork/Join сейчас является одной из самых распространённых методик для построения параллельных алгоритмов
![Page 39: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/39.jpg)
Work stealing - планировщики на основе захвата работы
ForkJoinPool может в небольшом количестве потоков выполнить существенно большее число задач
![Page 40: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/40.jpg)
Work stealing • Планировщики на основе захвата работы (work
stealing) "автоматически" балансируют нагрузку за счёт того, что потоки, оказавшиеся без задач, самостоятельно обнаруживают и забирают "свободные" задачи у других потоков. Находится ли поток-"жертва" в активном или пассивном состоянии, неважно.
• Основными преимуществами перед планировщиком с общим пулом задач: – отсутствие общего пула :), то есть точки глобальной
синхронизации – лучшая локальность данных, потому что в большинстве
случаев поток самостоятельно выполняет порождённые им задачи
![Page 41: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/41.jpg)
public class ForkJoinCalculate extends RecursiveTask<Double> {
...
static final long SEQUENTIAL_THRESHOLD = 500;
...
@Override
protected Double compute() {
if ((end - start) / step < SEQUENTIAL_THRESHOLD) {
return sequentialCompute();
}
double mid = start + (end - start) / 2.0;
ForkJoinCalculate left =
new ForkJoinCalculate(func, start, mid, step);
ForkJoinCalculate right =
new ForkJoinCalculate(func, mid, end, step);
left.fork();
double rightAns = right.compute();
double leftAns = left.join();
return leftAns + rightAns;
}
}
![Page 42: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/42.jpg)
protected double sequentialCompute() {
double x = start;
double result = 0.0;
while (x < end) {
result += step * func.apply(x);
x += step;
}
return result;
}
![Page 43: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/43.jpg)
ForkJoinPool pool = new ForkJoinPool();
ForkJoinCalculate calc = new
ForkJoinCalculate(sqFunc, start, end, step);
double sum = pool.invoke(calc);
How to launch recursive execution
![Page 44: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/44.jpg)
Spliterator
public class ForkJoinCalculate extends RecursiveTask<Double> {
...
static final long SEQUENTIAL_THRESHOLD = 500;
...
@Override
protected Double compute() {
if ((end - start) / step < SEQUENTIAL_THRESHOLD) {
return sequentialCompute();
}
double mid = start + (end - start) / 2.0;
ForkJoinCalculate left =
new ForkJoinCalculate(func, start, mid, step);
ForkJoinCalculate right =
new ForkJoinCalculate(func, mid, end, step);
left.fork();
double rightAns = right.compute();
double leftAns = left.join();
return leftAns + rightAns;
}
}
Collector
![Page 45: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/45.jpg)
0
0,5
1
1,5
2
1 2 4 8 16 32
t (s
ec)
Threads
Execution time
Simple Threads
Thread Pool
Fork/Join
![Page 46: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/46.jpg)
0
1
2
3
4
5
6
2 4 8 16 32
Spe
ed
up
Threads
Speedup
Simple Threads
Thread Pool
Fork/Join
![Page 47: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/47.jpg)
Fork/Join effectiveness
• Local task queues and work stealing are only utilized when worker threads actually schedule new tasks in their own queues – If this doesn't occur, the ForkJoinPool is just a
ThreadPoolExecutor with an extra overhead – If input tasks are already split (or are splittable) into
tasks of approximately equal computing load, then it less efficient than just using a ThreadPoolExecutor directly
• If tasks have variable computing load and can be split into subtasks, then ForkJoinPool's in-built load balancing is likely to make it more efficient than using a ThreadPoolExecutor
![Page 48: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/48.jpg)
The F/J framework Criticism • exceedingly complex
– The code looks more like an old C language program that was segmented into classes than an O-O structure
• a design failure – It’s primary uses are for fully-strict, compute-only, recursively
decomposing processing of large aggregate data structures. It is for compute intensive tasks only
• lacking in industry professional attributes – no monitoring, no alerting or logging, no availability for general
application usage
• misusing parallelization – recursive decomposition has narrower performance window. An
academic exercise
• inadequate in scope – you must be able to express things in terms of apply, reduce, filter,
map, cumulate, sort, uniquify, paired mappings, and so on — no general purpose application programming here
• special purpose
![Page 49: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/49.jpg)
F/J source code
![Page 50: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/50.jpg)
F/J restrictions
• Recursive decomposition has narrower performance window. It only works well: – on balanced tree structures (DAG), – where there are no cyclic dependencies, – where the computation duration is neither too short nor too long, – where there is no blocking
• Recommended restrictions: – must be plain (between 100 and 10,000 basic computational steps in
the compute method), – compute intensive code only, – no blocking, – no I/O, – no synchronization
![Page 51: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/51.jpg)
F/J restrictions
• Recursive decomposition has narrower performance window. It only works well: – on balanced tree structures (DAG), – where there are no cyclic dependencies, – where the computation duration is neither too short nor too long, – where there is no blocking
• Recommended restrictions: – must be plain (between 100 and 10,000 basic computational steps in
the compute method), – compute intensive code only, – no blocking, – no I/O, – no synchronization
All problems
![Page 52: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/52.jpg)
F/J restrictions
• Recursive decomposition has narrower performance window. It only works well: – on balanced tree structures (DAG), – where there are no cyclic dependencies, – where the computation duration is neither too short nor too long, – where there is no blocking
• Recommended restrictions: – must be plain (between 100 and 10,000 basic computational steps in
the compute method), – compute intensive code only, – no blocking, – no I/O, – no synchronization
F/J
All problems
reduce, filter, map, cumulate, sort, uniquify, paired mappings, …
![Page 53: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/53.jpg)
Lambda
![Page 54: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/54.jpg)
![Page 55: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/55.jpg)
1994
“He (Bill Joy) would often go on at length about how great Oak would be if he could only add closures and continuations and parameterized types”
Patrick Naughton, one of the creators of the Java
![Page 56: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/56.jpg)
1994
“He (Bill Joy) would often go on at length about how great Oak would be if he could only add closures and continuations and parameterized types”
“While we all agreed these were very cool language features, we were all kind of hoping to finish this language in our lifetimes and get on to creating cool applications with it”
Patrick Naughton, one of the creators of the Java
![Page 57: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/57.jpg)
1994
“He (Bill Joy) would often go on at length about how great Oak would be if he could only add closures and continuations and parameterized types” “While we all agreed these were very cool language features, we were all kind of hoping to finish this language in our lifetimes and get on to creating cool applications with it” “It is also interesting that Bill was absolutely right about what Java needs long term. When I go look at the list of things he wanted to add back then, I want them all. He was right, he usually is”
Patrick Naughton, one of the creators of the Java
![Page 58: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/58.jpg)
Ingredients of lambda expression
• A lambda expression has three ingredients: – A block of code
– Parameters
– Values for the free variables; that is, the variables that are not parameters and not defined inside the code
![Page 59: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/59.jpg)
Ingredients of lambda expression
• A lambda expression has three ingredients: – A block of code
– Parameters
– Values for the free variables; that is, the variables that are not parameters and not defined inside the code
while (x < end) {
result += step * (sin(x) * sin(x) + cos(x) * cos(x));
x += step;
}
![Page 60: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/60.jpg)
Ingredients of lambda expression
• A lambda expression has three ingredients: – A block of code
– Parameters
– Values for the free variables; that is, the variables that are not parameters and not defined inside the code
while (x < end) {
result += step * (sin(x) * sin(x) + cos(x) * cos(x));
x += step;
}
step * (sin(x) * sin(x) + cos(x) * cos(x));
![Page 61: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/61.jpg)
Ingredients of lambda expression
• A lambda expression has three ingredients: – A block of code
– Parameters
– Values for the free variables; that is, the variables that are not parameters and not defined inside the code
while (x < end) {
result += step * (sin(x) * sin(x) + cos(x) * cos(x));
x += step;
}
step * (sin(x) * sin(x) + cos(x) * cos(x));
A block of code
![Page 62: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/62.jpg)
Ingredients of lambda expression
• A lambda expression has three ingredients: – A block of code
– Parameters
– Values for the free variables; that is, the variables that are not parameters and not defined inside the code
while (x < end) {
result += step * (sin(x) * sin(x) + cos(x) * cos(x));
x += step;
}
step * (sin(x) * sin(x) + cos(x) * cos(x));
A block of code
Parameter(s)
![Page 63: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/63.jpg)
Ingredients of lambda expression
• A lambda expression has three ingredients: – A block of code
– Parameters
– Values for the free variables; that is, the variables that are not parameters and not defined inside the code
while (x < end) {
result += step * (sin(x) * sin(x) + cos(x) * cos(x));
x += step;
}
step * (sin(x) * sin(x) + cos(x) * cos(x));
A block of code
Parameter(s) Free variable
![Page 64: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/64.jpg)
Lambda expression
step * (sin(x) * sin(x) + cos(x) * cos(x));
![Page 65: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/65.jpg)
Lambda expression
step * (sin(x) * sin(x) + cos(x) * cos(x));
x -> step * (sin(x) * sin(x) + cos(x) * cos(x));
![Page 66: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/66.jpg)
Lambda expression
step * (sin(x) * sin(x) + cos(x) * cos(x));
x -> step * (sin(x) * sin(x) + cos(x) * cos(x));
Function<Double, Double> func =
x -> step * (sin(x) * sin(x) + cos(x) * cos(x));
![Page 67: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/67.jpg)
Lambda expression
step * (sin(x) * sin(x) + cos(x) * cos(x));
x -> step * (sin(x) * sin(x) + cos(x) * cos(x));
Function<Double, Double> func =
x -> step * (sin(x) * sin(x) + cos(x) * cos(x));
F(x) G(y)
![Page 68: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/68.jpg)
Lambda expression
step * (sin(x) * sin(x) + cos(x) * cos(x));
x -> step * (sin(x) * sin(x) + cos(x) * cos(x));
Function<Double, Double> func =
x -> step * (sin(x) * sin(x) + cos(x) * cos(x));
F(x) G(y)
Function<Double, Double> func =
x -> sin(x) * sin(x) + cos(x) * cos(x);
Function<Double, Double> calcFunc =
y -> step * y;
Function<Double, Double> sqFunc =
func.andThen(calcFunc);
![Page 69: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/69.jpg)
SequentialCalculate sc = new SequentialCalculate (
new Function<Double, Double>() {
public Double apply(Double x) {
return sin(x) * sin(x) + cos(x) * cos(x);
}
} );
![Page 70: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/70.jpg)
SequentialCalculate sc = new SequentialCalculate (
new Function<Double, Double>() {
public Double apply(Double x) {
return sin(x) * sin(x) + cos(x) * cos(x);
}
} );
SequentialCalculate sc =
new SequentialCalculate(x -> sin(x)*sin(x) + cos(x)*cos(x));
![Page 71: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/71.jpg)
SequentialCalculate sc = new SequentialCalculate (
new Function<Double, Double>() {
public Double apply(Double x) {
return sin(x) * sin(x) + cos(x) * cos(x);
}
} );
SequentialCalculate sc =
new SequentialCalculate(x -> sin(x)*sin(x) + cos(x)*cos(x));
Function<Double, Double> func =
x -> sin(x) * sin(x) + cos(x) * cos(x);
SequentialCalculate sc = new SequentialCalculate(func);
![Page 72: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/72.jpg)
Stream API
![Page 73: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/73.jpg)
Integral calculation double step = 0.001;
double start = 0.0;
double end = 10_000.0;
Function<Double, Double> func =
x -> sin(x) * sin(x) + cos(x) * cos(x);
Function<Double, Double> calcFunc = y -> step * y;
Function<Double, Double> sqFunc = func.andThen(calcFunc);
double sum = ...
![Page 74: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/74.jpg)
Integral calculation double step = 0.001;
double start = 0.0;
double end = 10_000.0;
Function<Double, Double> func =
x -> sin(x) * sin(x) + cos(x) * cos(x);
Function<Double, Double> calcFunc = y -> step * y;
Function<Double, Double> sqFunc = func.andThen(calcFunc);
double sum = Stream.
iterate(0.0, s -> s + step).
![Page 75: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/75.jpg)
Integral calculation double step = 0.001;
double start = 0.0;
double end = 10_000.0;
Function<Double, Double> func =
x -> sin(x) * sin(x) + cos(x) * cos(x);
Function<Double, Double> calcFunc = y -> step * y;
Function<Double, Double> sqFunc = func.andThen(calcFunc);
double sum = Stream.
iterate(0.0, s -> s + step).
limit((long) ((end - start) / step)).
![Page 76: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/76.jpg)
Integral calculation double step = 0.001;
double start = 0.0;
double end = 10_000.0;
Function<Double, Double> func =
x -> sin(x) * sin(x) + cos(x) * cos(x);
Function<Double, Double> calcFunc = y -> step * y;
Function<Double, Double> sqFunc = func.andThen(calcFunc);
double sum = Stream.
iterate(0.0, s -> s + step).
limit((long) ((end - start) / step)).
map(sqFunc).
![Page 77: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/77.jpg)
Integral calculation double step = 0.001;
double start = 0.0;
double end = 10_000.0;
Function<Double, Double> func =
x -> sin(x) * sin(x) + cos(x) * cos(x);
Function<Double, Double> calcFunc = y -> step * y;
Function<Double, Double> sqFunc = func.andThen(calcFunc);
double sum = Stream.
iterate(0.0, s -> s + step).
limit((long) ((end - start) / step)).
map(sqFunc).
reduce(0.0, Double::sum); ∑ sum
![Page 78: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/78.jpg)
java.util.function.*
public interface Function<T, R> {
R apply(T t);
}
public interface DoubleUnaryOperator {
double applyAsDouble(double x);
}
![Page 79: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/79.jpg)
DoubleStream public interface DoubleUnaryOperator {
double applyAsDouble(double x);
}
![Page 80: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/80.jpg)
DoubleStream
DoubleUnaryOperator funcD =
x -> sin(x) * sin(x) + cos(x) * cos(x);
DoubleUnaryOperator calcFuncD = y -> step * y;
DoubleUnaryOperator sqFuncDouble = funcD.andThen(calcFuncD);
double sum = ...
public interface DoubleUnaryOperator {
double applyAsDouble(double x);
}
![Page 81: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/81.jpg)
DoubleStream
DoubleUnaryOperator funcD =
x -> sin(x) * sin(x) + cos(x) * cos(x);
DoubleUnaryOperator calcFuncD = y -> step * y;
DoubleUnaryOperator sqFuncDouble = funcD.andThen(calcFuncD);
double sum = DoubleStream.
iterate(0.0, s -> s + step).
limit((long) ((end - start) / step)).
map(sqFuncDouble).
sum();
public interface DoubleUnaryOperator {
double applyAsDouble(double x);
}
![Page 82: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/82.jpg)
What's the difference?
1,7
1,75
1,8
1,85
1,9
1,95
2
2,05
2,1
2,15
2,2
Execution time
Sequential
Generic Stream
Double Stream
![Page 83: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/83.jpg)
Stream parallel
double sum = DoubleStream.
iterate(0.0, s -> s + step).
limit((long) ((end - start) / step)).
parallel().
map(sqFuncDouble).
sum();
![Page 84: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/84.jpg)
![Page 85: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/85.jpg)
![Page 86: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/86.jpg)
![Page 87: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/87.jpg)
Stream parallel
double sum = DoubleStream.
iterate(0.0, s -> s + step).
limit((long) ((end - start) / step)).
parallel().
map(sqFuncDouble).
sum();
![Page 88: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/88.jpg)
and …
![Page 89: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/89.jpg)
and …
![Page 90: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/90.jpg)
and …
http://mail.openjdk.java.net/pipermail/lambda-dev/2013-June/010019.html
![Page 91: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/91.jpg)
![Page 92: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/92.jpg)
Stream parallel v.2 double sum = LongStream.
range(0, (long) ((end - start) / step)).
parallel().
mapToDouble(i -> start + step * i).
map(sqFuncDouble).
sum();
![Page 93: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/93.jpg)
Stream parallel v.2 double sum = LongStream.
range(0, (long) ((end - start) / step)).
parallel().
mapToDouble(i -> start + step * i).
map(sqFuncDouble).
sum();
![Page 94: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/94.jpg)
Spliterator
Collector
Streams parallel v.2 double sum = LongStream.
range(0, (long) ((end - start) / step)).
parallel().
mapToDouble(i -> start + step * i).
map(sqFuncDouble).
sum();
![Page 95: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/95.jpg)
0,35
0,355
0,36
0,365
0,37
0,375
0,38
0,385
0,39
Execution time
Simple Threads Thread Pool
Fork/Join Double Stream Parallel
![Page 96: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/96.jpg)
http://stackoverflow.com/questions/20375176/should-i-always-use-a-parallel-stream-when-possible?rq=1
![Page 97: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/97.jpg)
Parallel stream problem https://bugs.openjdk.java.net/browse/JDK-8032512
• The problem is that all parallel streams use common fork-join thread pool and if you submit a long-running task, you effectively block all threads in the pool.
• By default, all streams will use the same ForkJoinPool, configured to use as many threads as there are cores in the computer on which the program is running.
• So, for computation intensive stream evaluation, one should always use a specific ForkJoinPool in order not to block other streams.
![Page 98: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/98.jpg)
0
0,5
1
1,5
2
2,5
Execution time
Sequential
Generic Stream
Double Stream
Simple Threads
Thread Pool
Fork/Join
Double Stream Parallel
![Page 99: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/99.jpg)
Thank you!
![Page 100: От Java Threads к лямбдам, Андрей Родионов](https://reader036.vdocuments.net/reader036/viewer/2022062303/554e770db4c905f66a8b4e4a/html5/thumbnails/100.jpg)
javaday.org.ua