Multithreading for Beginners

freeCodeCamp.org · Beginner ·⚡ Algorithms & Data Structures ·1y ago

Key Takeaways

The video 'Multithreading for Beginners' by freeCodeCamp.org covers the basics of multithreading in Java, including thread creation, synchronization, and concurrency. The course teaches the theory and provides code examples to help students become confident and comfortable with multithreading concepts.

Full Transcript

multi-threading is an important Concept in computer science in this course you'll learn everything you need to know about multi-threading in Java but the concepts apply to other programming languages as well for each concept you'll learn the theory and then see some code examples hello and welcome I hope you're doing super good my name is rendu and I'm working as a senior engineer with Uber I have been programming for more than a decade now I believe that multi-threading is one such concept which is way too abstract and difficult to understand if it is not taught in a proper manner however if the concepts are explained with relatable examples it becomes a fun and engaging experience that's what I have done in this course I have broken down the difficult and Abstract Concepts in simple English which is really easy to understand to make the things even more clear I have presented relatable examples I strongly believe that multi-threading is one such tool which should be in the toolkit of every good programmer the entire video is split in a smaller sections wherein I teach about a particular topic each topic is explained with some theoretical concept followed by the examples and then I implement the topic of discussion in Java to give you a proper working code example the topics are taught in a bottomup manner where I start from the very Basics and then I build on the concepts layer by layer by the end of this tutorial you would become very confident and comfortable with the concepts of multi-threading and that's my guarantee to you the code examples are in Java however most of the concepts should be transferable in other languages as well which support multi-threading in some capacity so with that in place let's get started so what is the motivation for multi-threading by default programming languages are sequential in nature code execution happens line by line in usual scenario consider the below code so in this method we have init de call then we have download data call then we have process on data and then finally show the results so in the usual scenario all these things will be executing one by one so first this will be called then this will be called then this then this but we have a problem in a single threaded program these instructions will be executed one by one and the time consuming section of the code can freeze the entire application what is the solution well figure out the timec consuming tasks and decide if they can be run separately if yes run such tasks in separate trades let's have a quick Layman explanation of how a timec consuming a step in your code can slow down or freeze your entire process let's say you invited your friend over to your place to watch this super cool movie being a great host you decided to make some popcorn for your friend but here is the catch it will take some 5 to 7 minutes to prepare the popcorn during the time you are involved in preparing the popcorn your friend asks which movie are we going toward wors today since you are super involved in making the popcorn you don't respond your friend even though feeling a bit weird about the situation asks you again if you're okay but thanks to your involvement in the process of making the popcorn you don't respond situation becomes super strange however your friend tries one final time and asks you if there did something wrong and thanks to your deep dedication in the process of popcorn making you don't respond by this time your friend gets freaked out and punches you in the face and you reboot but we all know this does not happen in real life unless you are playing a prank we humans are naturally equipped to multitask in this example since you would be aware of the time it takes to prepare popcorn you would probably prepare the recipe and put the pot on the stove and let the popcorn get prepared while it's getting prepared you are available ble to do anything if there is a need so you figured out the task which is going to be timec consuming started its execution and let it finish in its own line of execution effectively you did not block other tasks on you and did not freeze entirely if you follow line by line execution of tasks in your program this kind of freezing situation may arise in your code if there is a task which takes longer time to execute so what is the Improvement so in this case let's go through the different calls so in it de DB is where you are initializing certain DB related things then you have download data then you process the data then you show the results so to me it looks like downloading of the data is something which could take the major chunk of time what we can do now is put this download data in some sort of other threade and everything else in some other threade and in that sense we can do a parallel processing and it will ensure that by the time we are waiting for downloading the data everything else is not getting Frozen up and as a Sy is not lagging so this is one such Improvement we could do by the virtue of multi-threading so to give it a formal definition multi-threading is the ability of CPU to perform different tasks concurrently now let's have a quick explanation around concurrency versus parallelism concurrency is like having multiple tasks to do but you only have one set of hands you switch between the tasks doing a little bit of each one at a time if you play a guitar it's similar to that where you play different notes and cords using your nine fingers even though you play each note separately the switch is so fast and smooth that overall it appears as if everything is being played together parallelism on the other hand is again having multiple tasks but now you have many friends to help you out each friend works on a different task at the same time so all the tasks get done faster so in summary concurrency is doing multiple things all at once by quickly switching between the tasks and parallelism is doing multiple things at At Once by having different parts of the task been done simultaneously by different entities now let's learn about concurrency versus parallelism in somewhat more technical terms so concurrency and parallelism are two terms which are used quite a lot and that to interchangeably while discussing multi- threading but there is a subtle difference let's talk more about it concurrency refers to the ability of a system to execute multiple tasks at the same time or nearly overlapping times so they seem like being executed at the same time in concurrent systems tasks may start execute and complete independently of each other but they may not necessarily be executing simultaneously at any given moment concurrency is often achieved through techniques like multitasking where a single processor switches between executing multiple tasks rapidly or through the use of multiple threads or processes parallelism on the other hand refers to the simultaneous execution of multiple tasks to achieve faster performance of increased throughput in parel system tasks are truly executed simultaneously either on multiple processors or multiple processor course or through other means of parall processing like distributed computing or GPU Computing parallelism is all about breaking down a task into smaller non-related subtasks which can be executed concurrently to speed up the overall execution time thus in the context of a hardware with a single CPU code currency could be understood as a perceived parallelism or fake parallelism even more so in scenarios where tasks appear to be running simultaneously but are actually being executed sequentially or in an interleaved manner this is done by something called as time slicing algorithm so in summary concurrency is about managing multiple tasks or processes potentially interleaving their execution to give an appearance of simultaneous execution whereas parallelism on the other hand is about truly executing multiple tasks or processes simultaneously to achieve a fast performance while the terms are related and often used together they refer to distinct Concepts in the context of computing now let's understand what is a process and thread process is an instance of program execution when you enter an application it's a process the operating system assigns its own stack and Heap area whereas threade is a lightweight process it is a unit of execution within a given program a single process may contain multiple threads each thread in the process shares the memory and the resources of the parent process one single process could contain many other threads now let's learn a bit about the time slicing algorithm let's imagine we have multiple threads associated with the process somehow the CPU has to ensure that all these threads are given a fair chance to execute one such approach is to use the time slicing algorithm so uses time for the CPU is shared among the different threads so here is what happens so you see sharing is time slicing let's say the green boxes represent one thread and the Yellow Boxes represent another thread thread T1 and T2 respectively and consider that this is the timeline and at this particular time thread T1 is assigned to the CPU then after some time thre T1 takes a break and we assign thread T2 to the CPU and after some time T2 is given some rest and thread T1 is assigned again to the CPU so as you see it's going into a back and forth manner where each and every threade is taking turns to run on the CPU one by one so here what we are doing is we are basically slicing the time and we are assigning certain time Quantum to the CPU so here we have a CPU and these are the two different threads which are kind of taking its turn to be executed on the CPU so this is how the time slicing algorithm works now what happens when we have enough CPU at our disposal so let's say we have thread one and we have thread 2 and there are two CPUs so in that case thread 1 will run entirely on CPU 1 and thread 2 will run entirely on CPU 2 so it's effectively a parallel kind of processing wherein we are not sharing anything on a given CPU rather each threade has has a dedicated CPU and it does not need to bother about whether it has to share the CPU with the other thread or not and please note that I have put CPU here but it could be a different core in the CPU itself so it could be either different cores of a given CPU or it could be different CPUs so that depends on the hardware in such kind of setup we can achieve the parallel processing now let's look at some of the pros and cons of multi- threading the first one is we can build responsive applications so now you don't have to worry about freezing uh situation and thus you can build your applications to be responsive second is you will have a better resource utilization because now with the use of multi-threading you could ensure that your Hardware or your CPU is not sitting idle rather once it's idle it could be taken up by some other thread for execution and the third thing is it helps us into building performant applications so with the help of multiple core CPUs we can build parallel programs and essentially we could get some benefit on the side of performance as well now coming to the cons of the multi-threading the first one is synchronization needs to be done and it can get tricky at times so essentially when you are doing multi-threading you need to share the memory space and other resources with the process and in that case let's say when there is a process and there are certain number of threads you need to share the resources so so we need to ensure that we are not running into funny situations and those things are handled by something called as synchronization we will have a much more focused discussion around all these things later in the video the second thing is it is difficult to design and test multi-threading apps so essentially you don't have a control in which the different threads could execute so in that sense it's difficult to predict the behavior of the threads so it's difficult to design and test multi-threaded applications and the third thing is thread context switch is expensive so if there are more than required number of threads then it becomes detrimental to your system performance so multi-threading is not a silver bullet which will help you with all the situations rather we should use it judiciously now let's have a look on the thread life cycle any thread will start its lifetime in the new state and every threade is in this state until we call start on it after we have called a start on it it goes to something called as active State and this active state has two substates either it could be runnable or running as we saw in the earlier slides in some cases we may have to do some sort of time slicing and in that case there could be five threads which are ready to run but there is no CPU available on which it could run and we have called a start on such threads so those trades will be runnable State and there could be certain threads which will be in running State and as soon as those running State threads are done then they could allow the threads in the runnable state to run again and this is what we mean when we say that it has two substates which is runnable and running effectively this is the active State and the third state is the blocked state so every threade is in this state when it is waiting for some thread to finish so let's imagine there are two threads T1 and T2 and then they both started running on the CPU and after some time T1 got a chance and it was executing its task after some time it had to be taken out of the CPU and T2 got a chance but now T1 is not completed it's waiting for its execution to complete because T2 is now on the CPU so T1 is in a blocked State and this is what we mean by the blogged state now T1 will get a chance to execute on the CPU and maybe it may be done with its entirity of execution and then it goes to a state called as terminated state so every threade is in this state after it's done doing its required task here we are in the ID I have created a normal Java project and it's called as multi-threading So the plan is that for the entire duration of this tutorial I'll be using the same project and I'll be creating different packages inside the project to discuss the concepts of the multi-threading so in this particular section we will be discussing about the sequential execution so in order to demonstrate the code let's create a class call it a sequential execution demo and here is the idea behind this particular class I will be creating certain methods and the intent of this particular class is to Showcase that in a normal Java program the execution happens line by line and there is no jumping around from this part of the code to the other one so let's get going so to begin with I'll create the main method and in the main method I will have two methods let's call those as demo 1 and demo 2 and let's create those two methods I won't be doing anything fancy I'll just create a normal for Loop which will be iterating in certain range and then it will print some message and that's it so let's copy this one and let's change the name and here let's change the message as well and now let's run the program on running this this is the outcome that we have so first we were executing demo one method so the entirety of demo one is executed wherein it will be printing from 0 to 4 with this message which is from demo 1 plus I so from demo 1 0 from demo 1 2 till 4 and likewise we have executed demo 2 and in that case we print this message which is from demo 2 and the IAT number which is from demo to 0 to from demo to 4 so what we see is that the execution happens line by line so the main method is the first one to get started and the first thing it sees is that we are invoking a method called as demo one it goes there it executes it it comes back then the next line it says is that it's demo 2 it goes to demo 2 it executes it and it comes back here and then the execution terminates so this is what we mean by the sequential execution so in the context of multi-threading what we can understand is that each and every program is single threaded unless otherwise instructed so here we just have a single threade and that is the threade that is created by the jvm for the execution of this main method and this could also be called as the parent thread or maybe the main thread now let's learn about the way in which we can create threads in Java and the first way is to implement a runnable interface so we will create a class let's call it as runable thread example and let's have a main method created the way it works is that we will have to Define some sort of class and the class will Implement our enable interface so let's do that so let's call it as thread one and it will implement the runnable interface and the runable interface has one method which we need to implement so that's run method and the logic is whatever we Implement inside the run method that is executed by this thread so let's do that so let's have a for Loop which runs from i0 to I4 and it prints a message let's call it as thread one and I would be the ith time it has been called now let's create an another thread let's call it as trade two which implements runnable and let's implement the run method and here as well we can run from i0 to I4 and let's print the message call it as thread to and I so this is a way in which we can Define the threads and once the threads are created they need to be somehow started so in order to do that what we can do is we can define a thread let's call it as one then new thread and we can pass the class that we have created so thread one and likewise we can say thread two new thread new thread two and we have the handle for these two threads 1 and two so how do we start these threads well we have a method called as start so let's do that so what happens is once you call the start method jbm will start these two threads and they are in the runnable state so they could be either immediately running or they will have to wait because they don't have any CPU available at their disposal where they could go and run so let's run this and see what is the outcome like so what we see here is first thread one is running then we have thread two running but this may not be the case always so in order to see a clear example let's increment the number of times we are going to print this message so let's increment it to 10 and let's do this to 15 now let's run the program and see the outcome so here here is what we see first we have thread one running and then thread two takes over and then thread one is running and then thread two takes over and so on and so forth so once everything is executed the execution will stop and the program will terminate so what we are observing is we have created two threads we have started the threads but there is no sequence in which they are executing rather the thread has been created and it's available to be scheduled by the thread scheduler and once the thread scheder finds an available spot for a particular thread to be run on the CPU it's assigned to the CPU and that's the time it's running for the time when it does not have the access to the CPU the thread will have to wait and that is the reason we are seeing a back and forth execution pattern wherein first one thread will run for some time then thread two will take over and then thread two will wait for some time and then thread one will take over there is also a different way in which we can create a threade using the runable interface and that is by making use of the anonymous in a classes so let's create trade three then new trade three and what we can do is new runable and let's print a similar kind of message so I less than let's say 15 I ++ the message could be 33 plus I and we can do the same thing which is 3. start because three is the handle that we have given for this particular thread one thing which you can observe is that this could be easily turned to a Lambda so let's do that and here we have a much cleaner way of creating a thread using the runnable interface so all we need to do is inside the Lambda we can provide the logic which needs to be executed by that particular thread now let's run it and see its outcome so we can see thread 2 is running then thread 1 is running then thread three is running and every thread gets some time of execution with the CPU and eventually all the threads are executed and terminated so this is how we can create threads in Java by implementing the runnable interface the other way of creating a thread in Java by extending the thread class with the help of extend scale keyword let's learn about the same so let's create a class and let's call it as extends thread example let's create the main method and now let's create the different threads so let's call it as thread one extends thread class and likewise we had to overwrite the run method in the example of runable approach we need to do something similar here as well so let's do that and we can have a for Loop which runs from I as 0 to 9 print some message let's copy this let's call This Thread two let's change the message as well so we have created two threads which is thread one and thread two now we need to instantiate it so let's call this as thread one and then new thread one then threade two and New threade 2 please note that here we are directly creating the thread as we are not passing this object inside the thread Constructor like we were doing in the case of runnable approach so now once we have the handle for the threads we can call do start on these two let's run this method and see the results so what do we see we have thread one running then thread 2 takes over now again we have threade one and then threade two takes over finally everything is executed and the program gets terminated so the basic idea Remains the Same once we call do start on these trades they are in the runnable state and based on the availability of the CPU they will be submitted to one CPU and they could start with their execution now that we have seen both the approaches of creating a threade one by implementing the renewable interface other by extending the thread class let's see which approach is better so if we extend thread then we cannot extend any other class usually it's a big disadvantage however a class May Implement more than one interface so while using the implements runnable approach there is no restriction to extension of class now or in the future so in most of the cases runnable is a better approach to create a thread now let's learn about do join method in Java let's create this class let's call it as join thread example and first we create create the main method let's create thread one and we can use the anonymous in a class or maybe Lambda to create the thread So This Thread is going to print from 0 to 4 and it will have a message call this as thread 1 followed by I let's copy this one let's call this as thread 2 let's change the message as well now thread 2 and this is going to be printed for let's say 25 times and first we call do start on the one then we call do start on the two and then let's have a message which says done executing the threads so what do you think is going to be the output for this particular program if you're new to multi trading then you could say that first these two threads will be executed and we will see all these messages getting printed on the console and finally we will have this message printed but looks like it's not going to be the case so let's run it and find it out and definitely it's not the case in fact done executing the threads is the first thing to get printed on the screen so why is it happening so in order to understand this we will have to take a step back and understand how does the main method works so in this particular program main method is the first thing which is getting called by the jbm and when this happens this main method is run by your main threade so this main threade is the first one which gets assigned to the CPU with the highest priority we will learn about priority and all those things in some time but for now understand that this main thread has the highest priority so it starts with its execution first thing it does is it creates the definition for threade one second thing it does is it creates the definition for this trade two and it comes to line number 17 then to 18 and in these two lines it moves these two threads in the runable state and finally on line number 19 we have this message since the main thread has the highest priority for now this message is printed first so what happens is all these trades are executing independent of each other so threade one will start with its execution independently so will thread two and the main thread anyway has the access to the CPU for now it's going to print this one as soon as possible and that is the time it's done with its execution and it waits for these two threads to complete their execution and once that is done then the program is going to terminate so what should I do if I have this functionality wherein I want that thread one should be completed and only after that happens the main method or the main thread should proceed with its execution so in order to implement that functionality I can make use of dot join method so let's call do join on thread one and do join method throws an interrupted exception in order to correct this we can either surround this with TR catch or could add the exception being thrown in the method signature itself I'll go with the second option now with this in place let's run the program and see what is the outcome so this is what happens threade one needs to be executed five times and threade two needs to be executed 25 times so first threade one is executed one is getting executed and looks like by this time thread one is executed and at the same time thread 2 got hold of the CPU so it started executing and you notice that for the next time thread one was was supposed to be executed but threade one did not have anything left for execution effectively it was done with its execution and that is where the dot join came in effect and it instructed to the jvm that one is done with its execution now it's time for the main thread to take over and proceed with its execution which is printing this particular line so now we print this line and then thread 2 proceeds with its execution it's going to print all the messages by thread 2 and as soon as this is done both the threads are completed and now the main thread also shuts down so what we learned here is 1. join is kind of hinting to the jvm that as soon as I am done with my execution then you can start with the execution of other threads which are in the Que in this case we had two and the main thread so first two was executed for a while on the CPU then the main thread started with its execution which was printing this line so what happens if I place two do join as well so in that case jbm will not mly wait for the thread 1 to get completed rather it will also Wait For Thread 2 get completed and only after that this message will be printed so let's try that out as well let's rerun the code and see what is the outcome so you see all the threads are executed so basically one and two are finished and only after that we see the outcome that is done executing the threads now let's print a message before executing these threads and what we can say is before executing the threads now let's run it so you see first we see the message before executing the threads now all the threads are executed because we have placed dot join on both of these two and finally done executing the threads is being printed so basically why this is happening is till this point of time we have not put the threads into the runable state so the main thread is the only active thread in this context and this is the reason we printed this as soon as weed at this line so now let's understand about the join operation with some theoretical Concepts so first thing to notice is that main thread is the parent thread so when we start a program usually the execution begins with the main method this method runs on the main thread this can be understood as the parent thread since it responds the other threads as well then the other important point to notice here is the independent execution of threads under normal circumstances so when you create and a start the threads they run concurrently with the main thread unless instructed otherwise so under normal circumstances all threads run independent of each other more explicitly no thread waits for other thread so what is join method well imagine threads to be lines of execution so when we call do chwine on a certain thread it means the parent thread which is the main thread in this case it's saying hey thread once you are done executing your task join my flow of execution it's like the parent thread waits for the completion of the child threade and then continues with its execution and here is my perspective on this concept well personally I find the join keyword is not very intuitive at first for the kind of operation it's doing somewhat better terms could have been wait for completion or complete then continue what's your perception about the joint method in Java let me know in the comment section now let's learn about the concept of ton and user threads on the basis of surface of execution threads can be of two types demon threads or user threads demon threads usually run in the background wherein user threads are the active threads so when a Java program starts the main thread starts running immediately we can start children threads from the main threade the main threade is the last threade to finish its execution under normal circumstances because it has to perform various shutdown operations demon threads are intended to be helper threads which can run in the background and are of low priority for example garbage collection thread demon threads are terminated by the jvm when all other user threads are terminated or they are done with their execution so under the normal circumstance stances user threads are allowed to be terminated once they are done with their execution however the demon threads are shut down by jbm once all the other threads are done executing now let's have a quick code demo for the concept of demon threads and user threads so let's create this class called as demon user crate demo and uh first of all we will create the main method now let's create two threads first is demon helper implements run let's overwrite the run method the second thread is user thread helper lements runable let's overwrite the the run method now let's write the code which needs to be executed by the demon threade let's have a counter variable start with zero and while count is less than 500 do a thread do sleep and then do a account Plus+ finally print this message which is demon helper running the sleep will expect us to pass certain time let's give the time for 1,000 milliseconds and the Sleep Method throws an interrupted exception so let's surround this with try catch for the user thread method let's have a sleep timer for let's say 5,000 milliseconds and let's surround this with dry catch then print this message user thread done with execution now let's create these threads in the men thread so first we start with the demon thread let's call this as a background thread that is bz thread so new thread new demon helper for the user helper maybe we can call this as user thread new thread new user helper by default any threade is not a demon threade the way to make a normal threade as a demon threade is by calling do set demon method so let's do that bz thread. set demon and true finally we will start these two threads so BG thread. start and user thread. start now let's run this program so here is what happens the user thread has a sleep timer of 5 Seconds so as long as the thread was started it got assigned to the CPU and once it was assigned to the CPU it went into a sleep stage and then the demon thread was assigned the CPU and during that time there was no threade which was contesting for the CP CPU so the demon threade kept on running in the background so it ran for 4 seconds so remember we had the sleep timer for, millisecond so after every second this was running and printing the message demon helper running as soon as the 5sec got completed this thread came back into existence and it printed user thread done with execution and once this happened then the user threade helper got concluded it got finished with its execution and now what jbmc is is that there is no other threade which needs to be executed so by this time all the user threads which is basically just this threade is done with its execution and now it's time to shut down the jvm and that is where even though this demon thread was not completed because in the normal case of operation it could have ran till the count was less than 500 which was certainly not the case because it ran just for four times but still it was terminated because the user thread was terminated so this is what we learned from from this particular demo that user threads are given the priority demon threads run in the background and once all the user threads are concluded the jvm shuts down the main thread and along with that the demain thread is also forced to be shut down let's learn about a very important concept of thread priority so let's say there are 10 threads in runnable state however there is only one available CPU so only one threade can execute at a given point point of time others will have to wait so who decides which trade gets to run on the CPU well the component who decides this is called as the thread scheduler so each threade has a certain priority to begin with and under normal circumstances the thread with the higher priority gets to run on the CPU please note the keyword under normal circumstances the priority value varies from 1 to 10 and it can be assigned to any thread one priority is represented as the Min priority and 10 priority is represented as the max priority by default the priority of a thread is five and is represented as Norm priority Norm as normal threads of the same priority are executed in fifo manner so the thread scheduler rest stores the threats in a que now let's just spend some time to understand this part of normal circumstance so well what happens is all the main threads are started at a normal priority of five but still they are privileged to be executed first on the CPU by the thread scheduler why so well it's by Design since it's a main thread it has to be executed first otherwise your program will have to wait unnecessarily so this is the first thing which gets to be executed in any case even though the priority of the main thread is five it gets the first priority to be executed on the CPU for the first time now let's have a code example to have a demo of the thread priority concept so let's create this class called as threade priority example let's have a main method we can get the priority of the current threade by calling threade do current trade. get name so let's print this out now let's get the priority of the current trade so we can call trade. current trade. getet priority let's print this out as well now let's get the current threade so threade do current threade do set priority and we have Max priority let's print the updated priority let's run this so the outcome we will expect is the name of the main threade then the current priority which is the default priority because we did not change it earlier and after we change it what is the priority let's run and find out so the name is main to begin with the default priority is five we updated the priority to maximum and then we printed the same let's print something so thread dot current threade doget name says hi now let's create one trade say thre one says high as well and by default the new threade which is threade one will have default priority of five now let's give this the maximum priority possible which is trade do Max priority and then 1 do start so what do you think will happen now that trade one has the maximum priority and the main threade is still at at the default priority of five I should expect that this one should be executed first then I should see the outcome for this one so let's run it out and see what is going to happen well what we see is the first one to get executed is main says High then the second is threade one says high as well so please note that this property of main threade getting the first priority or the higher priority to get executed on the CPU regardless of this having the lower priority which is five as compared to the max priority is only for the first time this thing gets executed once execution is starts it will go by the fif manage and then the thread scheder will schedule the threads based on their priority and if they happen to be of the same priority fif will be used to schedule the threads so this is how we deal with priority in the case of threads in Java now that we have learned the basic concepts of multi- threading regarding how we can create trades and how we can wait for Threads to complete using the join keyword Etc now it's time to learn about the important concept of thread synchronization so let me create a package to write the code so let's call this as trade synchronization and I will Implement a class let's call this as synchronization demo so here is what I want to implement in this class I want to have a counter variable that will be initialized with zero and I will be using two threads to increment this number up to certain times and in the end I will check the final value of the counter variable so let's do this so first thing is let's create the main method and uh let's have a counter variable as well let's let's call this as private static int and counter let's initialize this to zero let's create two threads so as seen in the earlier video that we can create threads in line using the Lambda expression so let's do that and uh in this trade what I want to do is that I want to increment the value of counter let's say 10,000 times and let's call this as counter Plus+ let's do the same using another thread so I have a code template that I could make use of so that will save me some time to type the code again let's have this for Loop run 10,000 times and those many times we will increment the counter variable then in the last what I will do is I will start these two threads so let's name this as two oops and uh two. start and before printing the final value of the counter I want to make sure these two threads are completed so the way to do is I would call join on this so let's surr this with try catch let's duplicate it and let's call the same for trade two as well and finally we could print the counter value now let's run this one and see what is the outcome like so let's run this and see the output so the output that we have is 16583 but what we expect I is that since this trade is executing it 10,000 times and this trade is running this for 10,000 times then effectively it should have been 20,000 right but that is not the case what we are having is 16583 how about let's run it one more time and this time we are getting a totally different number let's run it one more time and we are getting a totally different number so this is not correct right then how can we correct this so even before we learn how can we correct this it makes sense to learn why this is happening so the reason for this behavior is something called as a nonatomic operation so what exactly is a non-atomic operation well what we see here is we are incrementing the counter variable counter is counter plus one even though it appears as if the counter variable will be incremented by one in one shot but that is not the case under the hood under the hood what will happen is first the counter variable will be loaded in the memory then it will be incremented by one then this will be assigned back to the counter variable which is holding this particular value and that is where we set the variable value back to counter plus one so essentially this is consisting of three steps so the first step you could think of it as load second step is the actual increment and then set back the value so let's say at this point of time thread one starts operating on this particular logic which is incrementing the counter variable let's say at that point of time the value is zero and uh it incremented the value the incremented value became as one and now it's time to apply this value incremented value back to counter variable but at the same time threade 2 came into the picture and what it did is it said hey let's load the value for counter and it found that the counter value still is zero and then it went ahead so let's call this as this being uh the operation by thread one and now thread 2 came into the picture it said hey let's load the value for counter and it is zero and what I'll do is I'll increment the value so after incrementing the value it became as one and this is where we have thread 2 doing this operation so effectively this value should have been two by this point of time but unfortunately since the time at which this threade one could have set the value as one and applied that value back to the counter variable and that is where the thread 2 came and it intercepted the uh execution and it found out that the value was Zero it incremented it back to one what should have been uh a two here that is still one right so this is the inconsistency and this is the inconsistency due to which we are seeing this error that we are not getting the final value of 20,000 here rather we are stuck at some lesser number so what exactly we call this phenomenon well so this thing is called as race condition in the terminology of multi- threading and concurrent programming so what we have here is a shared resource that is the counter we have two threads working on the same shared resource and these kind of scenarios lead to inconsistencies which are called as the risk conditions so how exactly can we fix this so the way to fix this is if you have different uh counters on which these two threads could work on then probably this could work on one counter this could work on another counter but that is not the case here right the whole point of discussing this example is that we have a shared resource and we want to make use of multi-threading concept to have some sort of operation applied to this particular share resource so in order to avoid this kind of situation what we can do is we could somehow ensure that this operation right this operation is being done by one and only one threade at a given point of time so that way what we could achieve is this is not being done simultaneously at the same time by two threads and in that sense we could achieve something called as Mutual exclusion and it will ensure that once thread one has the access to this particular logic which is incrementing the counter then it will do all the three parts which is loading the counter incrementing the value and setting back the value to counter so now counter has one and once thread one is done with its execution then the other thread could make use of this particular shared resource in its own execution so essentially we are restricting the users the access to the shared resource by multiple threads at a given point of time and how can we achieve this so one way to achieve this is with the help of keyword synchronized and uh we could do this at multiple levels like have this synchronization at the method level or the Block Level so now let's see how can can be fixed this using the synchronized keyword create an another method and uh let's call this as increment and what this will do is it will simply increment the counter variable let's replace this counter ++ with this increment let's do the same here as well and what we have here now is a method which is going to increment the variable and as I told earlier that we want to ensure that this particular operation is being called by just one method at a given point of time what we can do is we could restrict its access and the way to do this is put a synchronized keyword here so essentially by uses of the synchronized keyword what we are saying that hey jvm please allow this particular method to be accessed by just one and only one threade at a given instant of time at any cost and that is how we have achieved synchronization in a code so this thing is also called as critical section so one way to understand this is that this section is quite critical to the execution of the program in the context of multi-threaded environment so we are limiting its access or the users by one and one one thread at a given point of time so with that in mind let's go ahead and run the code and see what is the outcome like so let's run this and now we can see that we are getting the the exact value of 20,000 let's run it a couple of more times and each and every time we see the value remains to be 20,000 itself and there is no changes as such so it feels like we have fixed the issue of synchronization by using this synchronized keyword but there are a few inherent problems with this particular approach the approach that we are talking is using the synchronized keyword at the method level so in order to understand what are those problems let's first understand how does the synchronized keyword work in Java so first thing is let's understand the monitor locks so each object in Java is associated with a monitor which is a mutual exclusion mechanism used for synchronization so when a thread enters a synchronized block or a method it attempts to acquire the monitor lock associated with the object on which the synchronization is applied so imagine that there is a shared room that room could be used by just one person at a time then you have to enter inside the room using some sort of lock you use the lock you open the door you go inside and then once you come out you release the lock you hand over the key to someone else who wants to make use of the room next likewise in every Java object we have this monitor lock which is also sometimes called as the intrinsic lock which needs to be acquired by the thread which wants to make use of the synchronized block or the synchronized method Or the critical section when a threade enters the synchronized blck or method it attempts to acquire the monitor Lock And if the lock is available the thread acquires the lock and proceeds to execute the synchronized code so what do we mean by the lock is available well what it means is no other thread will be currently holding that lock so if the lock is not available that is another thread is holding that lock then the thread enters a blocked state and it has to wait until the lock becomes available the second step of this process is releasing of the monitor lock so when threade exits let's say the threade is going to exit after this execution is completed it has to release the monitor lock and thus it allows other threads waiting for to acquire the lock and to proceed with their execution so the monitor lock used by the synchronized keyword is sometimes referred to as the intrinsic lock or the monitored lock of the object instance each object in Java has its own intrinsic lock and the synchronized keyword acquires and releases this lock implicitely when used at the method level or in the synchronized block so what is the problem exactly if we use this synchronized keyword at the method level so the first problem is that it's a kind of course grained locking so when we use synchronized at the method level it applies the log to the entire method body even though in this case our method body is just a one line but in actual code base it could span to multiple lines so essentially what you are doing is you are blocking any other threade from entering that particular method when you have applied the synchronized block on that particular method but your critical section could be just two or three lines so in that sense it does not make sense to block an entire method which could probably let's say have 30 or 40 lines right so that is the first problem with using the synchronized keyword at the method level and needless to say this leads to reduced concurrency and performance bottlenecks and uh the second thing is kind of related to the first issue itself so when synchronized is used at the method level we lose the fine grin control needed in the more complex scenarios so for example you might want to synchronize only a specific section of the code within the method or you might need to synchronized multiple methods together as an atomic operation using method level synchronization we you don't have this granularity level and the third issue is let's say uh when a subass is overriding a synchronized method from its super class it must also explicitly declare the method as synchronized if it wants to maintain the synchronization Behavior and the failure to do so can lead to unexpected behavior and potential synchronization issues so you see there are multiple issues with the synchronized keyword when we use this at the method level so let's understand this with some code example in the same code let me do some refactoring let's call this as counter one let's create another variable let's call this as counter 2 and let's call this as increment one and this as increment two and uh what we can do next is we can duplicate this method and uh let's call this as increment one and it's going to increment the counter one let's call this as increment two and it's going to increment in the counter two let's pri

Original Description

Multithreading is an important concept in computer science. In this course, you will learn everything you need to know about multithreading in Java, but the concepts apply to other programming languages as well. For each concept you will learn the theory and then see some code examples. Code: https://github.com/shandilya07/MultithreadingInJava ✏️ Course created by @shandilyacodes ⭐️ Contents ⭐️ ⌨️ (0:00:00) Instructor & Course Introduction ⌨️ (0:01:49) Introduction to Multithreading ⌨️ (0:14:33) What's sequential Execution ⌨️ (0:17:27) Creating threads using Runnable interface ⌨️ (0:22:50) Creating threads using Thread class ⌨️ (0:25:13) Difference between two approaches of creating threads ⌨️ (0:25:46) Join method in Java ⌨️ (0:33:35) What are Daemon Threads? ⌨️ (0:39:12) What is Thread priority? ⌨️ (0:43:51) What are synchronised blocks? ⌨️ (0:54:09) Problems of using synchronised blocks ⌨️ (1:05:35) Wait & Notify ⌨️ (1:14:58) Producer & Consumer using wait & notify ⌨️ (1:26:31) Introducing Executor Service ⌨️ (1:30:20) Single Thread Executor ⌨️ (1:36:32) Fixed Thread Pool Executor ⌨️ (1:42:18) Cached Thread Pool Executor ⌨️ (1:47:56) Scheduled Thread Pool Executor ⌨️ (1:55:00) What's the Ideal Pool size? ⌨️ (2:00:01) Callable & Future ⌨️ (2:09:17) Introducing synchronised collections ⌨️ (2:15:42) Countdown latch ⌨️ (2:27:51) Blocking Queue ⌨️ (2:41:45) Concurrent Map ⌨️ (2:54:38) Cyclic Barrier ⌨️ (3:09:44) Exchanger ⌨️ (3:22:52) Copy on write array ⌨️ (3:36:58) Why do we need Locks? ⌨️ (3:39:45) Condition on Locks ⌨️ (3:58:58) Reentrant Locks ⌨️ (4:09:31) Read Write Locks ⌨️ (4:21:19) Visibility Problem in Java ⌨️ (4:29:10) Deadlocks in Java ⌨️ (4:48:22) What are Atomic Variables? ⌨️ (5:00:34) What are Semaphores? ⌨️ (5:17:08) What is Mutex? ⌨️ (5:19:37) What is ForkJoinPool ⌨️ (5:53:14) Good Bye & Thank you! ❤️ Support for this channel comes from our friends at Scrimba – the coding platform that's reinvented interactive learning: https://scrimba.com/freec
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Playlist

Uploads from freeCodeCamp.org · freeCodeCamp.org · 0 of 60

← Previous Next →
1 React: Production Server Setup Part 2 - Live Coding with Jesse
React: Production Server Setup Part 2 - Live Coding with Jesse
freeCodeCamp.org
2 cookies vs localStorage vs sessionStorage - Beau teaches JavaScript
cookies vs localStorage vs sessionStorage - Beau teaches JavaScript
freeCodeCamp.org
3 Browser history tutorial - Beau teaches JavaScript
Browser history tutorial - Beau teaches JavaScript
freeCodeCamp.org
4 Graph Data Structure Intro (inc. adjacency list, adjacency matrix, incidence matrix)
Graph Data Structure Intro (inc. adjacency list, adjacency matrix, incidence matrix)
freeCodeCamp.org
5 React: Parameterized Routing with Next.js - Live Coding with Jesse
React: Parameterized Routing with Next.js - Live Coding with Jesse
freeCodeCamp.org
6 React: Dealing with jQuery Issues - Live Coding with Jesse
React: Dealing with jQuery Issues - Live Coding with Jesse
freeCodeCamp.org
7 setInterval and setTimeout: timing events - Beau teaches JavaScript
setInterval and setTimeout: timing events - Beau teaches JavaScript
freeCodeCamp.org
8 Browser and Device Testing - Live Coding with Jesse
Browser and Device Testing - Live Coding with Jesse
freeCodeCamp.org
9 Last Minute Updates - Live Coding with Jesse
Last Minute Updates - Live Coding with Jesse
freeCodeCamp.org
10 Post Launch Updates - Live Coding with Jesse
Post Launch Updates - Live Coding with Jesse
freeCodeCamp.org
11 React: Setting Up Google Analytics - Live Coding with Jesse
React: Setting Up Google Analytics - Live Coding with Jesse
freeCodeCamp.org
12 React: Masonry Layout - Live Coding with Jesse
React: Masonry Layout - Live Coding with Jesse
freeCodeCamp.org
13 Load Balancing Digital Ocean Droplets - Live Coding with Jesse
Load Balancing Digital Ocean Droplets - Live Coding with Jesse
freeCodeCamp.org
14 try, catch, finally, throw - error handling in JavaScript
try, catch, finally, throw - error handling in JavaScript
freeCodeCamp.org
15 Load Balancing: SSL Passthrough Setup - Live Coding with Jesse
Load Balancing: SSL Passthrough Setup - Live Coding with Jesse
freeCodeCamp.org
16 Graphs: breadth-first search - Beau teaches JavaScript
Graphs: breadth-first search - Beau teaches JavaScript
freeCodeCamp.org
17 React: Masonry Layout Part 2 - Live Coding with Jesse
React: Masonry Layout Part 2 - Live Coding with Jesse
freeCodeCamp.org
18 React: WordPress API Live Search - Live Coding with Jesse
React: WordPress API Live Search - Live Coding with Jesse
freeCodeCamp.org
19 Creating WordPress Custom Post Types - Live Coding With Jesse
Creating WordPress Custom Post Types - Live Coding With Jesse
freeCodeCamp.org
20 Dates - Beau teaches JavaScript
Dates - Beau teaches JavaScript
freeCodeCamp.org
21 Miscellaneous Front End Updates - Live Coding with Jesse
Miscellaneous Front End Updates - Live Coding with Jesse
freeCodeCamp.org
22 Merging a Pull Request from GitHub - Live Coding with Jesse
Merging a Pull Request from GitHub - Live Coding with Jesse
freeCodeCamp.org
23 React + Prettier + Standard JS - Live Coding with Jesse
React + Prettier + Standard JS - Live Coding with Jesse
freeCodeCamp.org
24 React: Sortable Responsive Table - Live Coding with Jesse
React: Sortable Responsive Table - Live Coding with Jesse
freeCodeCamp.org
25 Geolocation Sorting by Distance - Live Coding with Jesse
Geolocation Sorting by Distance - Live Coding with Jesse
freeCodeCamp.org
26 Tradeoff Matrix - Agile Software Development
Tradeoff Matrix - Agile Software Development
freeCodeCamp.org
27 The Definition of Ready - Agile Software Development
The Definition of Ready - Agile Software Development
freeCodeCamp.org
28 Getting first React job without experience - Ask Preethi
Getting first React job without experience - Ask Preethi
freeCodeCamp.org
29 React: Google Analytics Click Tracking - Live Coding with Jesse
React: Google Analytics Click Tracking - Live Coding with Jesse
freeCodeCamp.org
30 Submitting a PR to an Open Source Project - Live Coding with Jesse
Submitting a PR to an Open Source Project - Live Coding with Jesse
freeCodeCamp.org
31 Should I go back to school to get CS degree? - Ask Preethi
Should I go back to school to get CS degree? - Ask Preethi
freeCodeCamp.org
32 Hero Section CSS Changes - Live Coding with Jesse
Hero Section CSS Changes - Live Coding with Jesse
freeCodeCamp.org
33 Working Agreement - Agile Software Development
Working Agreement - Agile Software Development
freeCodeCamp.org
34 A day at Pennybox with Co-Founder Reji Eapen
A day at Pennybox with Co-Founder Reji Eapen
freeCodeCamp.org
35 React: Sorting and Filtering Data - Live Coding with Jesse
React: Sorting and Filtering Data - Live Coding with Jesse
freeCodeCamp.org
36 React: Sorting and Filtering Data Part 2 - Live Coding with Jesse
React: Sorting and Filtering Data Part 2 - Live Coding with Jesse
freeCodeCamp.org
37 React: Building a New UI - Live Coding with Jesse
React: Building a New UI - Live Coding with Jesse
freeCodeCamp.org
38 Definition of Done - Agile Software Development
Definition of Done - Agile Software Development
freeCodeCamp.org
39 Getting started with jQuery (tutorial) - Beau teaches JavaScript
Getting started with jQuery (tutorial) - Beau teaches JavaScript
freeCodeCamp.org
40 Making a React Blog with WordPress Content - Live Coding with Jesse
Making a React Blog with WordPress Content - Live Coding with Jesse
freeCodeCamp.org
41 React, NextJS, CSS - Live Coding with Jesse
React, NextJS, CSS - Live Coding with Jesse
freeCodeCamp.org
42 jQuery events - Beau teaches JavaScript
jQuery events - Beau teaches JavaScript
freeCodeCamp.org
43 React/NextJS Routing and WordPress API Custom Types - Live Coding with Jesse
React/NextJS Routing and WordPress API Custom Types - Live Coding with Jesse
freeCodeCamp.org
44 React: Working with API Data - Live Coding with Jesse
React: Working with API Data - Live Coding with Jesse
freeCodeCamp.org
45 React: Refactoring Components - Live Streaming with Jesse
React: Refactoring Components - Live Streaming with Jesse
freeCodeCamp.org
46 jQuery effects - Beau teaches JavaScript
jQuery effects - Beau teaches JavaScript
freeCodeCamp.org
47 More React Refactoring - Live Coding with Jesse
More React Refactoring - Live Coding with Jesse
freeCodeCamp.org
48 animate in jQuery - Beau teaches JavaScript
animate in jQuery - Beau teaches JavaScript
freeCodeCamp.org
49 "Finishing" My React Site - Live Coding with Jesse
"Finishing" My React Site - Live Coding with Jesse
freeCodeCamp.org
50 Starting a New React Project (P2D1) - Live Coding with Jesse
Starting a New React Project (P2D1) - Live Coding with Jesse
freeCodeCamp.org
51 React Project 2 Day 2: Learning Material UI - Live Coding with Jesse
React Project 2 Day 2: Learning Material UI - Live Coding with Jesse
freeCodeCamp.org
52 The Agile Manifesto - Agile Software Development
The Agile Manifesto - Agile Software Development
freeCodeCamp.org
53 jQuery: get and set with http, text, val, and attr - Beau teaches JavaScript
jQuery: get and set with http, text, val, and attr - Beau teaches JavaScript
freeCodeCamp.org
54 React Project 2 Day 3 - Live Coding with Jesse
React Project 2 Day 3 - Live Coding with Jesse
freeCodeCamp.org
55 The INVEST approach to product backlog items
The INVEST approach to product backlog items
freeCodeCamp.org
56 React Project 2 Day 4 - Live Coding with Jesse
React Project 2 Day 4 - Live Coding with Jesse
freeCodeCamp.org
57 Chickens and Pigs - Agile Software Development
Chickens and Pigs - Agile Software Development
freeCodeCamp.org
58 React Project 2 Day 5 - Live Coding with Jesse
React Project 2 Day 5 - Live Coding with Jesse
freeCodeCamp.org
59 jQuery: add and remove DOM elements - Beau teaches JavaScript
jQuery: add and remove DOM elements - Beau teaches JavaScript
freeCodeCamp.org
60 React Project 2 Day 6 - Live Coding with Jesse
React Project 2 Day 6 - Live Coding with Jesse
freeCodeCamp.org

The video teaches the basics of multithreading in Java, covering thread creation, synchronization, and concurrency. Students learn how to create threads, implement synchronization mechanisms, and optimize system performance.

Key Takeaways
  1. Create a Java project and implement the Runnable interface
  2. Define a run method and create multiple threads
  3. Use the join method to wait for threads to complete
  4. Implement synchronization mechanisms using the synchronized keyword
  5. Optimize system performance by avoiding race conditions and using atomic operations
💡 Multithreading is essential for optimizing system performance and concurrency, and Java provides built-in mechanisms for thread creation and synchronization.

Related Reads

📰
Building a Power Grid Inside Minecraft with BFS Algorithms
Learn to build a power grid inside Minecraft using BFS algorithms and understand its relevance to cloud services
Dev.to · Carlos Cortez 🇵🇪 [AWS Hero]
📰
The Run-Length Encoding Trick: How Simple Strings Get Compressed
Learn how Run-Length Encoding (RLE) compresses simple strings, a fundamental technique in programming and data compression, and apply it to optimize storage and transmission of data
Medium · Programming
📰
75 Days of Leetcode — Day 4: #238 — Product of Array Except Self
Solve the Product of Array Except Self problem on LeetCode to improve coding skills and learn array manipulation techniques
Medium · AI
📰
I implemented the algorithm that broke the sorting barrier. Dijkstra still wins.
Learn how the author implemented an algorithm that broke the sorting barrier and compare its performance with Dijkstra's algorithm
Medium · Programming
Up next
Stump Grinder Carbide Wheel Grinds Hardwood To Chips
Innoforge Studio
Watch →