WEBVTT

00:00.120 --> 00:00.640
Hi everyone.

00:00.640 --> 00:01.240
Welcome back.

00:01.240 --> 00:06.640
In this lecture, let's explore one of the most powerful and performance oriented gatherers which is

00:06.640 --> 00:08.040
the map concurrent.

00:08.280 --> 00:15.160
This gatherer allows us to process elements in parallel, brings true concurrency to stream transformations

00:15.400 --> 00:18.120
without having to manage threads manually.

00:18.160 --> 00:25.040
Normally when we work with standard stream operations like map or filter, they are processing elements

00:25.040 --> 00:27.320
sequentially one after the other.

00:27.520 --> 00:33.320
But there are many use cases where we want to handle elements concurrently, especially where each element

00:33.320 --> 00:39.320
involves a time consuming operation such as a network call, file processing, or a database query map

00:39.320 --> 00:41.440
concurrent enables exactly that.

00:41.480 --> 00:47.960
It transforms elements concurrently, meaning multiple elements are processed at the same time by different

00:47.960 --> 00:48.640
threads.

00:48.680 --> 00:55.480
The most interesting part of map concurrent is that it gives you the fine grained control over the level

00:55.480 --> 00:56.400
of concurrency.

00:56.680 --> 00:59.040
So in this case, here is an example of a map.

00:59.040 --> 01:02.620
Concurrent you specify the total number of threads you want.

01:02.660 --> 01:05.220
So in this case, number two is what we have given.

01:05.220 --> 01:07.380
That means there is going to be two threads.

01:07.380 --> 01:10.580
That's going to be processing the logic which is processed movie.

01:10.820 --> 01:13.180
In this case the processed movie is a function.

01:13.180 --> 01:15.380
So this function could be a network call.

01:15.380 --> 01:18.060
It could be adding a delay or file based operation.

01:18.260 --> 01:25.700
If you have a use case like that then these kind of operators like map concurrent can come in handy.

01:25.900 --> 01:28.420
Let's explore the syntax step by step.

01:28.420 --> 01:33.620
So the first one here is a concurrency defines how many tasks can run in parallel.

01:33.620 --> 01:36.780
This means you define how many threads that's going to be spun up.

01:36.780 --> 01:39.860
And then which is going to take care of executing the function.

01:39.860 --> 01:45.060
So in this case, if you have to relate what a function is, here is this lambda gatherer is the one

01:45.060 --> 01:48.820
which is going to handle all the concurrency under the hood.

01:48.860 --> 01:55.220
It manages threads, synchronization and ordering automatically for you so you can purely focus on your

01:55.260 --> 01:56.500
transformation logic.

01:56.500 --> 02:03.270
It ensures that there is a safe and efficient parallel processing without needing to use a Completablefuture

02:03.790 --> 02:05.470
Executer service manually.

02:05.550 --> 02:08.950
With this, let's go ahead and explore this code in action.

02:08.950 --> 02:10.510
So I'm back in IntelliJ here.

02:10.510 --> 02:12.470
So in here let's go back to the top.

02:12.590 --> 02:18.830
So first thing is I'm going to show you what the logic is by executing this function which is the demonstrate

02:18.830 --> 02:19.710
sequential map.

02:19.710 --> 02:22.470
So if you go to this function so we have the movies.

02:22.470 --> 02:28.630
And what we are doing over here is that we are actually sleeping the code logic for 100 milliseconds.

02:28.790 --> 02:29.230
Okay.

02:29.430 --> 02:32.710
Once we have that then we are actually creating a map.

02:32.910 --> 02:40.230
And then we are basically having the key as the title and the value as processed movie genre, duration

02:40.230 --> 02:42.670
and everything that's getting added as a value.

02:42.710 --> 02:46.070
Okay, let's execute this code and then see how long it's going to take.

02:46.070 --> 02:48.670
So I have this code to track the start time.

02:48.830 --> 02:51.070
I have the code to track the end time.

02:51.070 --> 02:55.150
We will see what is the total time it took to complete this function.

02:55.150 --> 02:57.670
Let's go to the top and then execute this function.

02:57.690 --> 02:58.490
So there you go.

02:58.690 --> 03:01.290
The sequential map, which is a traditional processing.

03:01.290 --> 03:04.530
It took like one, two, three, five milliseconds.

03:04.530 --> 03:07.250
That's approximately a little over one second.

03:07.250 --> 03:11.330
So that's the time it took because we are sleeping for how many seconds.

03:11.330 --> 03:13.690
We are sleeping for 100 milliseconds.

03:13.810 --> 03:19.490
Now what we are going to do, we are going to be writing the same logic using the demonstrate map concurrent.

03:19.690 --> 03:19.890
Okay.

03:19.930 --> 03:22.650
We have the code for the start time and end time already here.

03:22.650 --> 03:24.650
We don't have to worry about tracking that.

03:24.650 --> 03:28.770
So the next logic is how are we going to write the map concurrent.

03:28.770 --> 03:30.650
So let's start with that code.

03:30.650 --> 03:35.970
So map sorry movies dot stream and then gatherer.

03:35.970 --> 03:40.170
So it's going to be gatherers dot map concurrent.

03:40.170 --> 03:43.170
And I'm going to be providing the value as two.

03:43.450 --> 03:45.170
That means two concurrent threads.

03:45.330 --> 03:48.090
And the rest of the logic is going to be pretty much the same.

03:48.130 --> 03:48.330
Right.

03:48.370 --> 03:53.090
So what we can do is we can actually take the logic from the sequential map that we have.

03:53.090 --> 03:56.490
So whatever that starts from here and ends here, let's copy that logic.

03:56.530 --> 03:57.580
Let's go to the top.

03:57.740 --> 03:59.620
So that's going to be the second argument.

03:59.660 --> 04:00.140
Okay.

04:00.340 --> 04:00.940
So there you go.

04:00.980 --> 04:02.420
We have the second argument here.

04:02.460 --> 04:03.740
Let's put this over here.

04:03.780 --> 04:04.340
There we go.

04:04.460 --> 04:06.780
And after that I'm going to be doing the for each.

04:07.220 --> 04:11.180
This logic is going to be very much similar to what we have down below.

04:11.260 --> 04:15.380
So I'm going to be copying this value and then put it over here.

04:15.580 --> 04:15.980
Okay.

04:16.140 --> 04:17.500
So it's the same logic.

04:17.540 --> 04:23.100
The only difference is that I'm using a gatherer here and I'm passing the map concurrent.

04:23.140 --> 04:23.580
Okay.

04:23.820 --> 04:25.260
Let's run this program one more time.

04:25.260 --> 04:29.460
So overall the sequential processing time it took was one, two, three, five milliseconds.

04:29.500 --> 04:31.740
Let's see, what is the logger we have?

04:31.740 --> 04:34.340
We have the concurrent processing time over here.

04:34.380 --> 04:35.940
Let's run this program one more time.

04:36.060 --> 04:41.540
So here you can see that the concurrent processing time it took six 34 milliseconds.

04:41.580 --> 04:44.300
This is half the time of whatever sequential.

04:44.300 --> 04:48.340
The reason being we have used the concurrency value as number two.

04:48.580 --> 04:49.060
Right.

04:49.100 --> 04:49.900
Which is two.

04:49.940 --> 04:53.940
Let's say I'm going to bump it up to four concurrent threads.

04:54.140 --> 04:54.660
Okay.

04:54.700 --> 04:55.460
Let's run this.

04:55.460 --> 05:00.000
You will see a performance which is much more quicker than the previous one.

05:00.000 --> 05:06.520
So in this case it took like three 28 milliseconds, which is really four times the performance improvement.

05:06.520 --> 05:09.320
Just by changing that value, let's say we have 12 movies.

05:09.320 --> 05:12.760
I know that let's say if I change the value to 12, what happens?

05:13.000 --> 05:18.880
So if I change it to 12 on average it took like 100 milliseconds, actually a little over 100 milliseconds.

05:19.120 --> 05:23.160
This is like ten times better than the performance of sequential processing.

05:23.160 --> 05:29.760
So this is the beauty of map concurrent where you have the ability to manage the threads, which will

05:29.760 --> 05:32.480
give you the performance out of the box for you.

05:32.480 --> 05:37.600
And let's say if you have, uh, more questions on this, on how to understand this, highlight this

05:37.600 --> 05:43.000
code and ask it, actually explain how this works under the hood.

05:43.160 --> 05:49.600
It will give you a quick explanation of how this overall gatherer dot map concurrent works under the

05:49.600 --> 05:49.960
hood.

05:50.000 --> 05:51.120
So you can see right.

05:51.120 --> 05:52.120
Create a word.

05:52.160 --> 05:56.210
This creates a virtual thread executor with the maximum concurrency of 12 threads.

05:56.250 --> 06:00.410
It uses a Java structured concurrency model introduced in the recent versions.

06:00.410 --> 06:04.530
Virtual threads are lightweight and managed by the JVM, not OS threads.

06:04.850 --> 06:06.050
This is execution flow.

06:06.090 --> 06:09.330
Each stream element is submitted to a thread pool as a separate task.

06:09.490 --> 06:13.010
Up to 12 elements are processed simultaneously by different virtual threads.

06:13.170 --> 06:17.250
Results are collected and reordered to match the original stream order.

06:17.290 --> 06:20.450
That's really key actually, so you are not losing the original order.

06:20.690 --> 06:21.450
Back pressure.

06:21.450 --> 06:23.170
If all the threads are busy.

06:23.210 --> 06:26.010
New task wait until a thread becomes available.

06:26.010 --> 06:34.050
So you get all of this by just using the map concurrent, and pass the concurrency and your whole function

06:34.050 --> 06:35.930
logic over here using Lambda.

06:35.930 --> 06:37.930
So this is the beauty of map concurrent.

06:37.930 --> 06:43.250
This is an exciting feature for me because I'm always interested in improving the performance of the

06:43.250 --> 06:43.730
code.

06:43.770 --> 06:48.650
This is a very handy feature when it comes to improving the performance in your stream logic.

06:48.650 --> 06:51.770
I hope you have a pretty good idea about how map concurrent works.

06:51.770 --> 06:53.210
This marks the end of this lecture.

06:53.210 --> 06:54.210
Thank you for watching.
