With parallel computing, you can leverage multiple compute resources to tackle larger problems in a shorter amount of time. In this course, the second in the Parallel and Concurrent Programming with Java series, take a deeper dive into the key mechanisms for writing concurrent and parallel programs. Instructors Olivia and Barron Stone make these (often abstract) concepts down-to-earth, demonstrating key ideas using common kitchen activities. Learn all about synchronization, thread pools, asynchronous tasks, evaluating parallel performance, designing parallel programs, and more. Upon wrapping up this course, you'll have a solid understanding of how to parallelize a sequential program.
Olivia Chiu Stone
Systems Engineer III at BLUE ORIGINOlivia Chiu is a programmer and engineer who has always had a passion for learning how things work.
She earned her bachelor's degree in engineering physics from Queen's University and her master's degree in mechanical engineering from McGill University. After university, she spent several years programming in LabVIEW and C# as a developer at National Instruments before becoming the project manager for the NI VeriStand development team. Outside of work, Olivia is passionate about educating and encouraging students, and dedicates her time to fostering our next generation of engineers.
Engineer | Author | VeteranBarron Stone is an electrical engineer experienced in both low-level digital hardware and high-level software.
He earned his bachelor of science in electrical engineering from Rice University and his master of science in electrical engineering from the Air Force Institute of Technology. He spent several years as an applications engineer and product-marketing engineer at National Instruments, where he developed marketing and training content for the FPGA-based NI FlexRIO modular instruments. Today, Barron is serving as an officer in the United States Air Force.
Skills covered in this course
Learn parallel programming basics“
- Baron and I just made a slow cooker full of delicious hot soup and I'm ready to dig in. - But we should take turns to make sure we each get our fair share of soup. In this scenario, we're two hungry threads competing for access to a shared resource, the soup, and the slow cooker lid will act as a mutex to protect it. Only the thread that holds the lid can check to see how much soup is left, determine if it's their turn to take the next serving and modify the amount of soup that's left by taking some. - Hmm, that was some really good soup. I think I'll have another serving. Oh, I see you haven't taken your share yet. What about now? How about now? Now? - Olivia's thread is wasting a lot of energy repeatedly acquiring the mutex to check for a certain condition, to see if her turn to take more soup, and she'll continue doing that until my thread eventually gets scheduled, so I can acquire the lid, see that it's my turn and take my serving. - What I was doing is called busy waiting or spinning, repeatedly acquiring and releasing the lock to check for a certain condition to continue. It isn't very efficient, especially if it goes on for a long time. - This is one of the limitations of using just a mutex. Sure, it restricts multiple threads from taking soup at the same time, but the mutex alone doesn't give our threads a way to signal each other to synchronize our actions. To do that, we can use another mechanism called a condition variable which serves as a queue or container for threads that are waiting for a certain condition to occur. Think of it as a place for threads to wait and be notified. The condition variable is associated with a mutex and they work together to implement a higher level construct called a monitor. Monitors protect a critical section of code with mutual exclusion, and they provide the ability for threads to wait or block until a certain condition has become true, along with a mechanism to signal those waiting threads when their condition has been met. Conceptually, you can think of a monitor like a room that contains the procedures and shared data that you want to protect. Only one thread can be in that room at a time to use those procedures and access the data. The mutex is a lock on the door. Other threads that try to enter the protective section while it's occupied will wait outside in a condition variable, which is like a waiting room. They might all be waiting for the same condition to occur before they enter the monitor room, or there might be multiple condition variables or multiple waiting rooms, waiting for different conditions to occur to acquire that same mutex. When the thread inside the monitor finishes its business in the critical section, it will signal one of the conditions that it's their turn to execute, then it releases its lock on the door to exit the critical section. One of the threads waiting for that condition that was signaled will wake up and take its turn in the monitor, locking the door behind it, so it can execute the critical section. Now, the condition variable has three main operations. Wait, signal, and broadcast. Before using a condition variable, I first need to acquire the mutex associated with it, check for my condition, I see that it's not my turn to take more soup, so I'll use the condition variable's wait operation, which releases my lock on the mutex and then puts my thread to sleep or a pause state and places it into a queue waiting for another thread to signal that somebody else takes the soup. - Since Baron released his lock on the lid before going to sleep, now I can acquire it, see that it's my turn to take some soup, so I'll do that. Then, I'll use the signal operation to wake up a single thread from the waiting queue, so it can acquire the lock. Depending on the language you're using, you will also see this operation called notify or wake. Baron, wake up, it's your turn to take some soup. Finally, I'll release my lock on the mutex. - Ah, my turn. The third condition variable operation, broadcast, is similar to the signal operation except that it wakes up all of the threads in the waiting queue. You may also see it called things like notify all or wake call. Now, in this little scenario, we only had two threads signaling each other on a single condition, that somebody took soup which then changes whose turn it is to take the next serving. A more common use case that requires multiple condition variables is implementing a shared queue or buffer. If multiple threads will be putting items in a queue and taking them out, then it needs a mutex to ensure that only one thread can add or remove items from it at a time, and for that, we can use two condition variables. If a thread tries to add an item to the queue, which is already full, then it can wait on a condition variable to indicate when the buffer is not full. And if a thread tries to take an item but the queue's empty, then it can wait on another condition variable for BufferNotEmpty. These condition variables enable threads to signal each other when the state of the queue changes.
Practice while you learn with exercise files
Download the files the instructor uses to teach the course. Follow along and learn by watching, listening and practicing.
Download the exercise files for this course. Get started with a free trial today.
Download courses and learn on the go
Watch courses on your mobile device without an internet connection. Download courses using your iOS or Android LinkedIn Learning app.
Watch this course anytime, anywhere. Get started with a free trial today.