jPrime 2026

Visualizing the Java Concurrency API, Christian Heitzmann

50:13 · 03 Jun 2026 – 04 Jun 2026 · YouTube

About this talk

This talk focuses on visualizing the Java concurrency API, emphasizing the importance of multi-threading in modern computing. The speaker explains that every modern computer, including smartphones, employs multi-core processors, necessitating an understanding of concurrency models for effective application development. He introduces concepts such as asynchronous event handling in Java frameworks like Spring and JavaFX, and demonstrates the use of a custom-built thread visualizer program designed to illustrate key concurrency concepts. The discussion covers interrupting threads and the significance of using executor services for better thread management, especially since Java 5. Various concurrency tools, including the Fork-Join framework and specialized collections, are presented, underscoring the advantages of thread-safe structures over basic synchronized methods. Finally, the speaker shares insights from his extensive experience in software development, advocating for best practices in implementing concurrent programming.

Full transcript

I'm happy that there are so many visitors today. Welcome to my talk visualizing the Java concurrency API. Let me at the beginning tell you why this talk is relevant to you and why this pointer is not working which is weird. Okay, I need to see now it works. Okay, why this talk is relevant to you. Um and multi- threading or parallel programming or um concurrency is not

only about performance. Actually, it's more than that. Um I can't see the gray here. I need to look at my laptop. Um imagine every computer that is being sold today, let it be a smartphone or let it be a big computer, a Raspberry Pi, whatever, is multi-core processors today. So those multi-core processors need work. This is why you need to deal with multi-threading. If you develop a

server for example, all the events that come in, all the requests are handled in asynchronous um way. So you have asynchronous event handling which is why you need to know the basics of threading. Um using threads helps you to simplify the application significantly because you can concentrate on on single uh ways of doing work. Several Java frameworks are multi-threaded by default. So let it be Spring for

example or different graphical user interfaces. I don't know if you you know swing for example or Java FX. I will show you Java FX in a second. They are multi-threaded by default especially event handling is done in a thread called the event dispatching thread. And you also might get some weird things if you are not aware of the basics. And even if you do plain vanilla Java,

um if you just do a single hello world program, I think you already have four threads running because the Java virtual machine itself already has several threads. One of them being the main thread and another one being the garbage collector thread. So thread safety and thread knowledge is crucial to you. Um you can find the slides and those examples today at the end of the day in

the late evening. then I will upload them at this link. But you do not have to take a picture now. I will show you this link again at the end of my presentation. And a few words about me. Um I, as you saw, I'm from Switzerland. I have my own little company in Switzerland. I'm self-employed. I'm a certified software developer. My I've been doing Java for over

20 years and Python for more than 5 years. I love software certifications because they give you a really really great boost. That's why I call myself a certified software developer. Um, with my company, I deal a lot with software documentation. So, I consult other companies in doing their documentation, right? And I work as a trainer for software development practices. This is me and this is my name.

I also know how to read and write curillic. So, this is my name in curillic writing. It's Christian Heightsman and [applause] Since this is my second time here at J Prime, I was here last year in Bulgaria. I thought meanwhile I should learn some Bulgarian. So that's what I also did. Um good day for example is um Dooren and thank you is uh Blago and wait I'm

I'm not finished yet. Wait. I can also say Bulgaria is a beautiful country or J Prime is the best Java conference or I'd like to order Masha Nascara and a large beer please. Or you're cute. Can I get your number? Or it works in my machine. Um, it's always clear from the context and all you need to say is bangaranga. So the title is visualizing the concurrency

API and for this [clears throat] in 2019 I wrote a program or I started writing a program it hasn't changed a lot since then which is called the threat visualizer. This is what the program looks like. First I want to show you the basics. Um thread visualizer can load classes that have been compiled. So the thread visualizer can load bite code and when I click on start

it calls the run method of this class and then it executes this byte code and then this one here is what I call an array or you could call it a cake or whatever. Um you can't see it properly here. It consists of 360 slots or slices or pieces or indices whatever you want to call it. So this is divided by 360. And what you actually need

to do, let me show you the first steps example. This is the run method. You have to call a method which is called compute index. And then you give it a number between 0 and 359. And what compute index does it is wasting some time on the CPU. So it is generating some million random numbers and when it's finished it gives the color of the thread that

performed that computation. So in this case we perform three computations at position 100, 200 and 300. And we also have another method which is called at separator position that just puts some gray lines just for optical reasons. Then we can see we can put it in half into quarters and so on. And let me show you how this works. And if I now click on start, you

see those three gray lines that I defined as the separator positions. And then this is 100, 200, and 300. If you look closely, you see that it takes a little bit of time. So this is the purpose of the whole thing. computing a slot, computing a index is wasting some CPU time and that's the way I can show you the concepts of the concurrency API. H got

it that's the principle. [clears throat] Now let's continue with the second example. Um again we have three gray lines just for optical reasons. And now I have a for loop from zero to get array size. This just returns 360. At the moment it's fixed, but if one day I decide to change the application, then I'm more flexible with this one. So from 0 to 360 and then

I call compute index at this position and you can expect it will fill in all those slots from left to right. So let's have a look if I start B and that exactly what we can expect. You might also notice something. The left indeed says the left slots are calculated faster than the right ones. So this is something on purpose. I want an asymmetric behavior of this

whole program so that I that you can see the concepts of this concurrency stuff better. If everything had the same computation time, then you couldn't see those concepts as clearly as you will be able to see within a few minutes. When I click on stop, this is the first topic. How do you interrupt threads? When I it triggers an interrupt. It sends a interrupt to the child

thread that got executed. But you see it's not working. Which brings us to my first message. There is the only clean way to stop threads in Java is by cooperation. So the thread that is being executed always needs to check on a regular basis if it has been interrupted. So this is a very long calculation as you can agree we have this for loop and what we

now put in here we regularly check in this case 360 times if our thread has been interrupted. Interrupting is just a signal which says hey please could you please stop? But it's up to the thread to really um follow this message. In this case, we now do. I need to recompile. Then I see the updated byte code here. And when I start, if I now click on

stop, it recognizes the interrupt and then it stops. H let's get into more detail regarding. So these the two examples I showed you this regarding interrupting threats. Um I show you these two examples here. So let's change the chapter. So the first thing is interruptible thread classes. I show you the code. It's this one here. Again we have a line in the at the center. Now I

have two methods. One is called compute left which computes in a for loop all the indices on the left side and we have a message compute right which computes all the indices on the right side. Then we create low-level threads. So two threads one is called left thread which contains the task the method compute left and one is the right side. Then we start those two threads.

Is the font big enough? Can you read everything? Good. Perfect. And then we wait for the threads to finish. This is what we are do by um thread punct join. And now let's just have a look. Um so now you see two threads in parallel. If we start then you see the red one is thread number one which does the left half and the green one in

this case is the thread that does the right one. And now you see the asymmetric behavior which is what I wanted. And now thread one is finished. And now we wait until thread two is finished. But again the stop button is not working. I could even restart it again. So now there's some weird behavior. Um if I do this like this. So stopping again is not working

correctly. So what do we need to do while we are waiting for our child threads to execute? We also need to be able to react if someone wants to interrupt us. So in this case uh interrupted exception will be thrown. And what do we have to do in this case? We have to reset the interrupt state and then we have to forward the interrupt to the child

threads that we created. So if I now add those three lines, if I recompile it and then let me show you how it works. And if I now click stop, it is also forwarded to the child threads and then they properly stop. So uh no and now I show you the same with executor services. Um you should not use the low-level threads as I just showed you

before because this is old school. And since Java 5 you have the very large concurrency API and you should use the concurrency API consistently. So do not use barefoots anymore. In this case we have the same methods compute left compute right. what you already know. In this case, now we create a so-called executor service with a fixed thread pool of two, which means there are two worker

threads who now do the work. And then we give it the two tasks. Compute the left side and compute Then we close our service. Closing the service just means do not accept any more tasks. And then we wait for a maximum of one hour until all the tasks have been finished. If we get interrupted while waiting, we do the same as we did before. We reset the

interrupt state. And now we need to forward the interrupt to the child threats to the worker threads. And this is what we do by service.sh shutdown. Now, so this is the hard shutdown. And now it sends interrupt requests to the worker threads and then they hopefully agree on it. In this case they do because I implemented the regular checks in my tasks. So let's have a look.

This should work without any changes. You see it looks the same as the pure threat version. And also stopping works. So everything fine. We can also speed it up. Bloop. it finishes neatly. So let's summarize. Hello. Yeah. Let's summarize. Um using executor services. they exist since Java 5. There has been a shutdown method since then. Since Java 19, there has been a clause method. You can also

use auto clausible now. So you can use executor services with try with resources. This is a non-blocking call and it finishes all tasks that have already been submitted but it does not accept any new talks tasks. The harder way is the immediate shutdown. This is what you get by shutdown. Now it's again a non-blocking call and it sends interrupt to all executing task but it doesn't guarantee

that they really terminate because as you know it's based on their cooperation that they check this on a regular basis. Awaiting termination is done with await termination. Give it a long time out could even be several days or nearly infinity if you give it some thousand days. This is a blocking call, but if you get interrupted, you will see that there's an interrupted exception. So, take home

messages for interrupting threads. Prefer executors over threats. Do not use low-level threats anymore. Consider interrupting child threats if you were interrupted. So, think h did I bear children? Do I also need to interrupt them? and close or shut down your executor services after use or after an interrupt. Now I talked a lot about executor services. Um this is a more schematic description of what they actually are

and the main idea is the following. Imagine you have a lot of small tasks and if you created a thread every time you had this task do the task and then let this thread die. This is wasting a lot of resources. So what the executor service does is once the service is created, it creates a certain number of threads once these are called worker threads. They are

in a thread pool and all they do is if they are in an idle state they just look at the queue of tasks, grab a task and execute it. And executing just means calling the run method or the call method on this task and then it's finished. The thread is idle and then it grabs another task from the queue. So there's no construction and deconstruction or destruction

of um those threads which makes it very very efficient especially if you have small There are several types of executors which I want to show you. So let's go to chapter C. Okay. And the source code is here. So I want to demonstrate the different types of executor services we have. H what I do now the whole cake is divided into eight pieces. So now we have

eight tasks to work on. I don't have compute left and compute right anymore. I now have compute subarray and then you give it the window size 1/8 28 3rd eth 4th eth and so on. You can believe me that this is correct. And what I do now I create an executor service. I give it those eight tasks. The subarray count is eight. I give it those eight

task with the specific window sizes. Then what you already know I close the service await termination etc etc. The first executor I want to show you is the single thread executor. And as you can imagine, it doesn't something weird. It has a single working thread that does all the work. if I show you this, this is how it looks like. Yeah. If you don't believe me, um

I can also change the direction of how the tasks are submitted. Now I do it from right to left. so now you can convince yourself that these are really that this is really a worker thread. And now it does the tasks from right to left. So you see these are the eight submitted task and there's one worker thread who's going through the list of this of these

tasks. So this is but this is not um something really interesting. Um what's more interesting is a fixed thread pool. You can give it the number of threads the number of working worker threads it should use. in this case too. But I can even make it more flexible. If I call my method from the thread visualizer which is called get thread count, it returns the number of

threads that I have set here in this spinner. So let's set it to two. Let's recompile. And if I now start, it's now set to two worker threads. And now we have two worker threads dealing with eight tasks. And now you see some very nice symmetric behavior. So eight two tasks work on no two threads work on eight tasks. We could make three tasks. Then it looks

like this. Now we have more parallelism of course especially on my CPU. I have eight CPU cores with hyperthreading. It's still an Intel machine. It's an old one which makes it 16 CPU cores from an operating system perspective. the maximum that makes sense here is eight. We got eight tasks. So the maximum parallelism we can achieve is eight. And if I execute this with eight worker threads,

they are all working in Yeah, there is another thing called cached thread pool. A [clears throat] cached thread pool creates as many worker threads as there are tasks. And you might think, hm, this is a great idea, but actually it is not. Imagine that you program a web server and if you only have three requests per second, that's fine. And then you have your three threads, your

worker threads that do the work. But if one day you get 1,000 requests per second, then it also creates 1,000 worker threads and then your system kind of crashes because I would say as a rule of thumb, you should not have more than 100 threads in your application. But there has been something new in Java since Java 21. I think it's been official. Um that's the virtual

threads and you get those virtual threads with new virtual thread per task executor. There was a deep dive yesterday if I remember right and there will be another talk this afternoon about virtual Yeah. So I won't talk a lot about it. A virtual thread just one sentence is a lightweight thread. It is not mapped onto the operating system. It is maintained by the Java virtual machine itself

and it is made for many threads that most of the time spent in a blocking state working for IO working for network working for database and so on then it is fine it's not doesn't make sense to have virtual thread for CPU intense tasks but my thread visualizer is a CPU inensive task so it works but I won't gain any advantage from it so I that it

works with virtual threads, but in this case there is no advantage. But if I have some thousand virtual threads that are not CPU intense, then there would be an advantage. [cough and clears throat] So excuse me. So let me also show you the schedule services. when you call blah blah scheduled executor you get a scheduled executor service back and this is something you can use um to

get some delay so in this case I have a method called compute random index which computes as the name says a random index and here I can specify a delay of 5 seconds and let's have a >> [clears throat] >> start. Then I also put a watch there. And after 5 seconds, it calculates a random index once and that's it. Now you think, well, can we do

more? Of course, we can do more. Um, this is when you use either schedule fix delay or the other one, which I will tell you in a second. So with schedule at fixed delay we have an initial delay of 5 seconds and then we set the repetition delay to 1 second. And it's important this 1 second comes after the task has finished. No matter how long the

task takes afterwards it puts a delay of 1 second. it's recompiled. Yes. So let's start. Then let's wait for 5 seconds. And now you see that after every computation there's a delay of 1 second until it starts with the new one. Now you might think, hey, this is cool. Let me program a little clock app with this with a minute hand that updates every second. But this

is not a good idea if you do it like this because updating the minute hand at the second hand, excuse me, also takes some time. So you get some time shift. So if you program a clock using this system, you will get a time shift, a significant time shift. So 1 minute won't be 1 minute, it will probably be 62 seconds or even more. So this is

where another method comes into play and this is called schedule at fixed rate. This has a pretty exact start of the task at 1 second. So this is probably what you want if you want to have it more exact. Let's recompile and show it to you. We have these five We have this 5-second initial delay. And now you see it starts every second. Probably you won't see

the difference now. But if I make the computations a little bit longer, then you see there's actually there's barely any pause in between because as soon as one task finishes, it has to catch up with the other tasks that have already been cued there. So every second a new task is being submitted no matter if the previous task have already finished or not. Good. Let's I showed

you the executor types the scheduled executor stuff and when I prepared for my Java 8 and Java 11 certification I created a diagram here and I thought I can give this to you. This is the basic model all the classes interfaces and so on. Perhaps you remember Runnable starting from Java 1. Then callable came with Java 5 which allows you to return values from those functions. A

thread is a runnable. Um you have executor service and the subclass which is scheduled executor service. You can only create those executor services with this factory class how I call it called executors with the s at the end. This is where you get all the specifics from. And the future, I haven't shown you this is kind of a receipt. If you submit a task, submit, not execute.

If you use the submit method, you get a future back, which is kind of the receipt. And then you can have a look if it's already been done, if it has been cancelled. And of course, you can also get the result of this task once it's it's there. What is not in there is this virtual flat stuff. So this is only valid until Java 18. For the

new Java versions, there should be another um factory method for the virtual thread stuff. And there also should be a close method for the executor service. Here we only have the old version, the shutdown version, but it's just minor details. Um let me show you some cool thing which is the fork join framework. And imagine if you have a task which is too large to be executed.

In my case, I have 360 array elements that I want to compute and this is too much work. And I could do the following. It's a divide and conquer approach. I say, you know what, let's split it in half and make two tasks out of it. So 180 pieces and another 180 pieces. And then I take this 180 piece which is still too big. And then I

think uh let's also make half of it. Then I make 90 and 90. Then I take the 90 piece which is still too big. Then I make 45 22 11 and so on until it is small enough and small enough means in my case if there's only one index that needs to be computed then I will actually compute it. And everything which is more than one slot

to be computed will be split and kind of rescheduled. And this is pretty impressive if I show you this in practice. First of all the this is what the code looked like. I show you some quarters indigoy and now we have a fork join pool which behaves from usage pretty much so from using the API pretty much the same as a executor service. So you call the

execute method you call close you call the shutdown await termination shutdown now and so on. So this is pretty similar to what you know from the executor services so far. But the magic is in this fork joint pool action. I cannot show you in detail because of time constraints. But what it's actually doing the important thing is the compute method. Again we check every time if we

someone wants to interrupt us and then we differentiate the cases. So the base case that's a trivial one. If we find out that there's only one slot that needs to be computed, then we compute it. H. If however we find out that the window that we should work on is more than one slot, then we split it in half. submit it again with invoke all. And this

is the left half of the window. And this is the right half of the window. And now I want to show to you how this looks like in reality. So I use two threads. and you see now that both threads or both worker threads one takes the left half and the other one takes the right half. Now look closely. Once the red one finishes its work, something

cool happens. It doesn't just stop and say, "I'm finished." Something else is happening. Now, just watch when the red one and then it grabs some of the remaining work that has been in the pipeline of B and works on that half. Now the green one is finished. Then it looks h I'm finished. What could I do? Who could I help? Oh, the green uh the the red

one has some work to do. So I go to the queue of the red one from the other side and take away half of its work. Now you see the red one will be finished next and then it will again grab something from the green's work and so on until they are finished. So they all finish their work more or less at the same time. This is

the cool thing. You wish you had co-workers like that, didn't you? So you can all go home in the evening at the same time. So I show you again without playing around how it looks like in 100% speed and see how the thread that is finished helps the other thread with doing the work. So it makes best use of the CPU. Want to see more threats? >>

What about four? Now it's getting more colorful. Also getting more chaotic. So now we have some nondeterminism. We cannot say which thread is helping which one. But what's most important is they will finish time pretty much the same. What about eight? Let's make it a little bit slower because now we got even more parallelism. So you see it's doing a pretty good job. And the maximum, which

makes sense in my machine, is 16 threads. Let's even make it even more slower. And now you see 16 They should not be idle and they should finish more or less at the same time. So using the fork chain, the fork join framework is actually the fastest way to get this work done. Yeah, that's it. So um if you want to read more about it many years

ago I think it was 2019 or 2020 I wrote an article about it in on my website um blog post the fork join framework you can read it there. Um once you have the slides you also have the link and this article was I translated it into German a few months or years later and then it was published in a German Java magazine called Java Spectrum. Um,

if anyone of you knows German or wants to learn German, then you can download the PDF of this article. This is the German version of this article and then I explain everything in great detail. So this is the demo I showed you and the take-home messages, executors and pools. Now you I showed you several things of [snorts] um the concurrency API. So what I can suggest have

a look at it study all the things and by study I mean read the official documentation. I need to emphasize this today because people today are just asking chat GPT and they get an answer and then they take this for granted and I think it's very important that you get an overview of what you can do. So you only get overviews by reading the official documentation, not

by using agents or chatbots or whatever. Play around with it. Practice it so that you get a feeling of it. Have a detailed plan or leave it up to experts. You have no idea how many projects I've seen that were messed up where there has been no plan. I can imagine that you also have that you know projects that didn't have a plan. Especially when it comes

to parallel programming, it has some very weird effects. And if you don't have a plan, if you don't have an overview or if just somebody did something, then this can be can have horrific consequences. So this is why I suggest always leave it up to expert. Expert doesn't mean that you have to have studied parallel programming for eight years. It just means there should be one guy

at the company. Guy means male or female, doesn't matter. Um there should be one person at the company who is more professional in this topic than the others. And it's enough if you declare one person say this guy is now responsible for everything that is parallel programming then you get some time to read about it you get some time to get an overview of it but that's

what I declare as an expert and always especially when it comes to when come when it comes to parallel programming keep it simple and consistent that's very important now for the last chapter I have a little riddle Um what is the final result of counter? I will explain the code to you. We have a class it's called incrementing counter. And the important thing is we have a

long field which is called Now we have a method called increment counter in loop. And what we do we run it. We have a loop of 100 million calls and we increment our counter 100 million times. If you call this method, of course, counter afterwards has the value 100 million because we incremented it 100 million times. But we do not call it directly. We call it eight

times in parallel. So we have an executor service with eight threads, worker threads, and we execute or submit this task eight Now the question is what do you think the result of counter is? Unfortunately I cannot make polls with you because acoustics is bad and timing is a little bit of a problem. Who thinks 800 million would make sense? Huh? We increment the variable >> I can't

understand. >> It is going to be less. >> It's probably to be less. Okay. Nobody thinks it's 800 million. You're also advanced. Why did you come here? Okay. What if you simply try it out? So theory would say should be 800 million. But practice perhaps doesn't. Um, so I show you the code in my IDE. First of all, it's chapter D incrementing counter. It's roughly the same

as you saw on the slides. This is the constant, the 100 million. This is the counter you saw on the slide. I skipped the parts with closing, termination, and all that stuff, but it's here in this correct version. So this is the code you saw in basic on the slide and I can execute it using my thread visualizer also. Now we don't see anymore those colorful things

but we just use it to start our code. So okay um we set eight threads. Okay. Now let's start. And then it takes 0.3 you see that the count is 111,000 111 million4,379. Now, if you work with parallel programming a lot as I do, you know, you should have known this number um because it's always 111,44. Are you kidding? no. Um, you see that the number is

not fixed. It's a low number, but it changes all the time. So what we have here is so-called race conditions. And the problem is that counter++ is not a single statement. It's actually in so the code it's three statements. It's like load the value of the variable, put it on the CPU register, then actually increment it and then write it back to the variable in memory. So

this is actually three steps to keep it simple. But as you know a scheduleuler can always put away a process or a thread from the CPU. So I let's call it pause. So there could be a pause after the first line or there could be a pause after the second line and then the other threads do their work and once it comes back to you again you

have some old value in your memory and then you override the variable with an old value. So other threats might perhaps already incremented the value several times and now it's back to you again and then you override it again with a value that has been less. But this are so-called race conditions. You also have them in practice in things like this. If you ever programmed a singleton,

you might have written things like if instance equals zero, create a new object. Otherwise, just return the object. This is how singletons work. However, this piece of code is not safe because if two threads call this at the same time and thread number one gets taken away the CPU here after the first line. Then the second thread comes creates a singleton and then it goes back to

the first thread which still knows yeah there hasn't been an instance yet. Let's continue with line number two. Let's create a singleton. And now what happened? You created two singletons, which is better. There also things put if absent. Um, put a value in a collection if it hasn't been there, remove a value if it's equal to some certain value, iterate over collections, navigate over sorted collections, and

so on. So, these are all so-called compound actions, and they can be very dangerous if done in parallel. So, be aware of this. Um, how can we solve it? In this case, we should not use counter++, but we should use a thread safe data type. In this case, it's atomic long. And the rest of the code is the same that we now write counter.increment and get. This

is a method that synchronizes everything and makes sure that there won't be any race conditions. And if I now run this in my simulation, the first thing you realize is that it takes significantly longer. You also realize that it takes a lot of But hopefully the result will be Oh, have a look. 800 million exactly as it should be. So there's a trade-off. Do you want fast

results or do you want correct results? It's the same as coding agents. You want to have a fast result. Thank you. I'm not saying anything to this. So I'm happy that you are at the non- aent talk here. Thank you. So um last example is concurrent collections. Um what I'm doing here is I have 50 million iter 50 million iterations. I put shorts in a tree set.

When you put something in a tree set it u first has to sort it. Sometimes it has to rebalance the tree. So all of that takes time. Um from the very beginning there have been so-called synchronized wrapper. You can put synchronized wrapper around a set around a list and so on and it just puts synchronized around every method statement over The good news is it works but

the bad news is it could be faster. So how much time do we need for this? 14.3 seconds. Now the point is now this is the topic regarding locks. Um you know synchronize perhaps where you can make things threat safe. You can synchronized on your own object. You can synchronize on other objects. You can also work with locks. Have a look at the re-ents and the re-entr

write logs. This is the UML hierarchy I once wrote down um for my exams. Perhaps one cool thing if you use a read write log it's actually two logs and if you have you can use an infinite amount of threads that just want to read because if there are several readers at the same time they don't disturb each other but there can only be one right only

one there can only be one writer and if the writer wants to have the right lock then all the read locks first need to go away then the right lock can do the writing. And once the rider is gone, the readers can come again or another rider can come. So this is a pretty cool thing. Um once you have time study. Now you might think synchronizing, yeah,

let's synchronize everything. But actually it's not that easy because there's a law called Amdal's law and it tells you that synchronize as little as possible because of the following. Imagine you have source code that is 95% parallelizable but 5% of it is synchronized. Synchronized means it can only execute in one single thread. And now imagine you have a machine consisting of 1,000 processors or 1 million doesn't

matter. So the part that is parallel could be perfectly parallelized and it's finished in zero But then still the maximum speed up that you get is only 20. You might think, I know, I got 1,000 CPUs. What about 900 speed up or something? I got 1 million CPUs. No, the maximum speed up you get is 20 because 12th of your application is still a bottleneck. Has to

be executed single threaded. So the maximum speed up you can gain is 20. So every line that you despite it not being necessary slows down your application significantly. Um so this is the reason why the rappers are a bad idea. last example you should lose the specialized thread safe collections. In this case, the thread safe version of a tree set is the concurrent skip list And if

we execute this, let me And if I start it now, we had 14.3 seconds with the synchronized wrapper. And with the optimized native version, we only need two seconds getting the same result. So this these are highly optimized data structures. So that's it. Um there are several concurrent collection. The only disadvantage is that they have some very weird names. So for tree map it's the concurrent skip

list map and so on. So you can list here and you can read it here. And for cues you have several cues um that are all thread safe. you can use those thread safe data structures also if you just have a single threaded So then there's no punishment if you use it. There's no performance delay even if you don't need parallelism. You can also use the those

parallelized thread safe versions. They even have another advantage because you won't get um concurrent modification exceptions if you use those. So this is what I can suggest. Try to use them as much as possible. So take home messages regarding locks and concurrent classes. I got the time in control will be two more minutes but um I got it on control. Watch out for hidden compound action. Either

replace them by atomic data structures or methods or guard them by locks. I prefer locks over synchronize by the way if you need to do it by hand. Keep the scope of synchronized blocks small but not too small. So make them as small as possible but as big as necessary and prefer concurrent classes over synchronized rapper classes. That's what I just told you. So take home messages

of the take-home messages. If you ever find yourself playing around with things like thread priorities, garbage collectors, put some delay routines. Yeah, if this module um if this module starts up 2 seconds later, then the whole application starts up properly. But if we don't delay it, then there will be some crash. Um stop. No, where are we? Stop. Um because this is not a safe way of

doing it. It works on your machine perhaps with your JVM version with your CPU but as soon as you put it on another machine or you put it on production you will have different threading behavior understand and use the higher level utility classes of the J to Java concurrency API as I said despite having CH GPT get the overview your own know what's going on there what

you need to take care of and again the most important rule keep it simple with this. Let me give you a very warm bangaranga which in this case means in this context means thank you so much that you came here to this presentation and again this is the link where you can take a picture and where in the late evening today you can download the slides and

the examples I showed you.

From event

jPrime 2026

03 Jun 2026 – 04 Jun 2026

All event videos
Back to Watch