[0:03]In this video, I will give you a quick overview of Java multi-threading and concurrency. But before I do so, I will just give you a quick overview of the history of multitasking and multithreading in general. Just a quick heads up, this video is an introduction to Java concurrency and multi-threading on a conceptual level. Therefore, it does not contain any code examples. However, this video is part of a playlist of videos about Java concurrency, and later videos in this playlist do contain code examples. If you check out the description below the video, you will find a link to this playlist as well as to my textual Java concurrency tutorials, which also contain code examples. In the early days of computing, a computer could only run one program or process as programs are also sometimes referred to. At a time, only one at a time, right? So, that in that case, if you were working inside of Word or something similar like that, and you needed to make some changes to something inside Excel, you would have to close down Word and jump into Excel and change the thing you needed to change, and then close Excel again and then open Word up again. And that kind of workflow quickly becomes quite annoying for the user. To solve this problem, multitasking was invented. And multitasking basically means that now the computers can run more than one application at the same time. But since the CPU can only execute one program at a time, the CPU or the computer solves this problem by executing one application for a little bit of time, then switching to another application and executing that for a little bit of time, then switching to another application and executing that for a little bit of time, then switching back to the first one and repeating. This way, it will appear to the user as if all of these application applications are actually running at the same time, even though they are actually only running one at a time, but the switches are so fast that the user cannot see or feel the difference. It feels as if all the applications are running at the same time. I said that it was the CPU that is switching between these applications here, these tasks, but in reality, it is a combination of the CPU and the operating system. Right? So, the CPU executes the operating system, and the operating system then switches between the tasks here. And modern day CPUs have some built-in features that makes this task switching easier for the operating system. Multithreading is the same principle as multitasking, except multithreading happens within an application. So, instead of having one thread of execution inside of an application, you can actually have multiple threads of execution inside of an application. So, for instance, an application might both be downloading a file in one thread and playing music in another. And the way that threads are executed is similar to how tasks or applications are executed. First, one thread here gets to execute a little bit on the CPU. And then the operating system, or the application, depending on who controls this thread, switches to another thread, and now this thread gets to execute a little bit. And now we switch completely, the operating system switches completely to another application, and then that first thread here inside of this application gets to execute a little bit on the CPU. And then it switches to another thread, and then this thread gets to execute a little bit on the CPU. And then we switch completely out of this application and back to the first one here, or to any other application running on the computer. Modern computers typically come with multiple CPUs in, or CPUs with multiple cores in, which in practice is the same thing. So, on a modern computer, you might actually have multiple CPUs executing these applications. And in that case, that means that some of the applications might actually be running at the exact same time because they're being executed by different CPUs. For instance, here, this application here might be running at the exact same time as this application down here because they are executed by different CPUs. Now, each CPU may still context switch between applications and internally between threads within each application. So, some of these applications and threads are not exactly executing at the same time, but some of them will be because of the multiple CPUs, right? By the way, in this diagram, it looks as if each application is only executed by a single CPU, but that is not necessarily the case. It is possible to have an application running, which has one thread executed by one CPU and another thread executed by another CPU down here, for instance. And in that case, each of the threads might actually be executing at the exact same time because they are running on different CPUs. And this is often the case in software that requires a lot of compute power, for instance, 3D rendering software, video editing software when the final video has to be exported or computer games. Now that we have discussed what multi-threading is, let's dive deeper into why multi-threading is a good idea. First of all, we can achieve better CPU utilization with multi-threading. Take a look at this example here. I have an application, which has one thread running here, and at some point here, the thread needs to load some data from disk or from network. Now, while this data is loading from the disk, the CPU cannot really do anything within this thread of execution. Up here, it needs the data before it can continue. But instead of simply being idle and wasting these CPU cycles, the computer can switch to another thread and execute whatever work this thread needs to get done. And at some point, when the CPU or the like, the operating system feels that it is time to switch back, or like when the full data here is loaded, it can switch back from this thread down here to the first thread, which can now continue running because now the data has been loaded into memory. In this way, we can better utilize the CPU while IO tasks are running. Another benefit we can achieve with multithreading is better IO utilization. Look at this example here. First, we have a thread running within an application, and now this thread needs to load some data from the disk. And then it spent some time processing this data, and after that, it loads the next data, and then it processes that data, etcetera. Now, as you can see, we are actually switching between full utilization of the CPU and full utilization of the network IO, but we have these gaps in between here, and here, and here, and here, where we are not using either. And imagine then that we have another thread. It's not the same thread, but another thread running which, you can see here, this thread up here is idle, right? The CPU is idle up here while it's loading data from the network. So, we can have another thread here, which has asked for some data, which it can now process. And then it this one can now load data, while this thread up here is not loading any data from the disk or network, right? It's actually utilizing this empty space here, idle IO bandwidth is being utilized by another thread, and so, um, and thus it can switch between these two threads that are utilizing the IO at different times. So, when one is waiting for IO, the other one is using the CPU, and when the other one is using the CPU, the other one can use the IO. And, uh, you know, the disk or the network. And actually, you might even have two threads using, um, the, for instance, the network card at the same time, for instance, if they are both downloading a file or waiting for a file to download from a server, which is slow, you might actually be able to to start multiple IO downloads at the same time from different threads.
[9:20]And so, in this way, we can not only better utilize the CPU, but also the IO capabilities of the computer, the bandwidth to the disk and to the network. A third benefit we can get from multithreading is higher application responsiveness as perceived by the user. Imagine you have a GUI application, a graphical user interface. The user clicks on a button here, it starts some, some, some processing here, and then it starts a long running task, which runs in the background, and when the task is finished, the thread can go back to updating the user interface and responding to further input from the user. Now, in this period here, the thread is busy executing the long running task, and thus it cannot respond to the user's input or update the user interface. And that means that in this time period here, the application will appear unresponsive to the user. Now, to avoid that, we can start the long running task in a separate thread down here, right? And thus the CPU will switch forth and back between the long running task in the separate thread and the main UI thread in the UI thread. And so, it switches forth and back, and to the user, it appears as if the application is still responsive because at all these times here, the application still responds to the user's input. But sooner or later, because this application will probably not use all of the CPU time, sooner or later, enough CPU time will have been allocated to the long running task in the separate thread down here, and so it finishes, and then the user interface can be updated saying, "Oh, by the way, this file is now fully downloaded," or whatever else this long running task in the background is. In a computer that contains multiple CPUs, the long running task here might actually be running on its own CPU, which is a different CPU than the one executing the main UI thread. And thus, we might start the action here, like this is just a little bit of initialization, then the long running task starts. And because this task is running on a different CPU, this thread is running in a different on a different CPU, the main UI thread can continued undisturbed on the other CPU. Or the its own CPU, which is executing this thread up here. So, now the long running task is actually executing at the exact same time as the main UI thread is continuing to respond to input from the user. As you can see, multithreading is really beneficial to us in computer programming. However, it is not without its problems, without its issues that we need to learn how to handle. First of all, the the first model that appeared for concurrency was the shared mutable state model in which threads could read and write the same memory inside of the application. However, this type of concurrency model leads to possible race conditions, invisible writes, and when you try to fix those two, you can get congestion, deadlock, nested monitor lockout, starvation, slipped conditions, missed signals, and a lot of other things. To solve these problems, either you can solve them with some constructs that help you handle these problems, or you can completely change the concurrency model to a no shared mutable state concurrency. And examples of such models are separate state concurrency, where the threads are only communicating with with messages. They don't share any actual data inside the application. They don't share access to any writable state in the application. You have functional parallelism, and parallel pipelines, etcetera. I will not get into further detail about either these problems or these concurrency models in this video. Um, but if you check out the description below the video, you will find a link to the playlist, my Java concurrency playlist, and, um, the videos in that playlist will explore some of these concepts in more detail. And additionally, if you find, if you check out the description below the video, you will also find links to my textual versions of these tutorials, which will cover many of the same topics, um, as the videos, but sometimes the text are a little bit ahead of the videos. That's all for this introduction to Java concurrency and multithreading. If you like the video, hit the like button and don't forget to check out the description below the video for a link to the full playlist with all the videos about, um, Java concurrency. And if you want to see more videos like this, why not subscribe to my channel?



