Learn the difference between processes and threads with an example Java program that spawns multiple threads and processes in this video. Also, observe that every Java process is a separate instance of the Java Virtual Machine (JVM).
- [Instructor] When you run a Java application, it executes within its own instance of the Java Virtual Machine, or JVM, and the operating system treats that instance of the JVM as its own independent process. So, if you run multiple Java applications at the same time, they'll each execute in a separate JVM process with their own independent memory space. Now, before I demonstrate a Java process with several threads in action, let's first take a look at the number of processors that are available on this computer, which I'll be using for the demonstrations throughout this course. To do that, I'll press Control + Shift + Escape to open the Task Manager, and then I'll select the Performance tab. At the bottom, I can see that this computer has 12 cores and 24 logical processors. It's quite a powerful computer. Those numbers mean this computer has 12 separate complete physical processing cores, and each of those cores supports something called hyper-threading, which enables them to each run two independent applications at the same time so that the computer, those 12 physical cores, are treated as 24 logical processors. Now, the hyper-threading in those 12 cores does not mean I'll get double the performance out of them. Hyper-threading takes advantage of unused parts of the processor, so if one thread is paused or not using a certain resource, then the other thread may be able to use it. So under certain workloads, that can create performance improvements, but it's highly application dependent. If you're using a Mac computer, you can check out the same properties in the Activity Monitor. The blue moving graph shows the total percentage of CPU utilization for all of those processors. I don't have much running right now so the usage stays low down near zero. If I want to see the CPU usage for each of those processors individually, I can get more information by clicking the Open Resource Monitor link and then selecting the CPU tab. The charts on the right show the total CPU usage on top, and if I scroll down, I can see how much each of those 24 logical processors on this computer are being utilized. The table on the left lists all of the current processes on this computer with information including each process's unique process ID number, its PID, its current status, the number of threads, and its average CPU usage. Now, to show you a few Java threads running in a process, I've created this short example program, which you can find in the Exercise Files Chapter Two, 02_02 and ProcessThreadDemo directory. This program defines a simple class on line six called CPUWaster, which can be used to create a new thread that spins in a while loop forever. It doesn't do any useful work, but the thread will stay alive forever and continuously use CPU cycles. For now, don't worry about the specific code for creating threads. We'll look at that in a later video. For this demonstration, we're just looking at how the threads behave. Down in the program's main method, on line 16 through 20, it prints out information about the program, including its process ID number, the number of threads in the process, and the total memory being used by that process. After printing the initial information, it creates and starts six CPUWaster threads with the for loop on line 24. I chose to start six of them because that's half as many physical processors as I have in this system, and I'll show you the effects of that in a moment. And finally, after starting those threads, the program prints out the process information again. Now, I'll build the project in IntelliJ by going to Build, Build Project, and normally I would run this in IntelliJ, however, IntelliJ creates an additional thread when running the program, so for just this demo, I'm going to close IntelliJ and then navigate to the directory where I created the output class file. Exercise Files, Chapter Two, 02_02, and ThreadProcessDemo, out, production, ThreadProcessDemo. I can see my two class files there. I'm going to open a Command Prompt within this directory by tying cmd into the navigation bar. And then I'll run the program by typing java ThreadProcessDemo. I can see that when this program is run, it's given a process ID of 5072. Initially, it only has one thread, the main thread, and it's using about 4,000 kilobytes of memory. Then, after I start six new CPUWaster threads, I still have the same process ID, but now my thread count is seven and I'm using an additional 1,000 kilobytes of memory. If I switch over to the Task Manager, I can see that the overall CPU usage is now sitting around 25%. That's because the six threads I created are running continuously and using CPU resources on six out of the 24 logical processors in this computer. I'll switch over to the Resource Monitor and I can see that the workload is being distributed across several of those processors. Now, if I look over at the Processes tab, I see a process called java.exe, which has the same process ID number as my program displayed earlier, 5072. However, under the Thread column, it says that it has 24 threads, which is more than the seven I saw earlier. That's because the Java Virtual Machine creates additional threads in the background to handle things like garbage collection and runtime compilation. From the program's perspective, running within the JVM, it only had seven threads, but to the operating system that's running the JVM, it sees those additional background threads for a total of 24. This program will continue running forever if I don't stop it, so I'll manually terminate it by closing the Command Prompt window that was running the program. If I look at the CPU usage now, I can see that when I close the program, those CPUWaster threads were terminated too, so the CPU usage dropped back down to around zero.
- Parallel vs. sequential computing
- Shared vs. distributed memory
- Execution scheduling
- Thread lifecycle
- Mutual exclusion
- Synchronized methods
- Checking for liveness