Unit 2: Concurrent Programming

1. First steps with threads

In this part, we are going to learn the basics of concurrent programming in Java, focusing on how to create and manage multiple threads in an application.

Introduction to concurrent programming

In this document, you have an introduction to concurrent programming. You will learn the concept of process and thread, their similarities and differences, and the most basic principles of concurrent programming.

  • In this document, you can learn how to instantiate external processes in Java (for instance, open a Notepad instance in Windows). You have Exercise 1 and 2 to practice with processes.

Basic thread management

  • In this document, you will learn how to create threads in Java programs in some different ways, and how to perform some basic tasks with them, such as pause them or stop them, along with accessing to some basic information like the thread name or status. You have Exercises 1 to 4 to practice with these basic steps. Also you have some additional exercises.

Thread coordination and synchronization

  • In this document, you will learn some techniques to coordinate and synchronize multiple threads:
    • First of all, you will learn how to join threads, so that a thread must wait for another thread to finish before it starts. Do Exercises 1 and 2 to practice with this concept.
    • Then, you will learn the need of synchronizing threads when they must access a shared value. Try to copy the example shown in the contents to see the problem in action, and then try to apply the synchronization mechanisms mentioned. Do Exercises 3 and 4 to practice with this.
    • Next step is not compulsory. It talks about how to establish thread priorities in some different ways, according to your operating system. This way, some threads can run faster than another threads, if they have higher priorities. Exercises 5 and 6 are focused on this concept.
    • The last section introduces you to the producer-consumer problem, in which some threads (consumers) must wait another threads (producers) to produce something before consuming it, and these threads (producers) must wait for the others (consumers) to consume before producing next items. There is a sample with shared data that you can try to copy and test, and then you can try Exercise 7 to deal with this problem.

2. Advanced thread management

In this part we are going to learn some advanced strategies for thread management, such as using thread executors, atomic variables and, concurrent collections.

Advanced thread coordination and synchronization

Estimated time: 3 hours

  • In this document, you have some more recent and advanced strategies to deal with threads:
    • First of all, you will learn about thread executors, which let us manage a bunch of threads as a pool, so that an object is in charge of starting threads and managing statistics about current active threads and so on. There are no exercises about this section, but you will see some examples about how to use executors in later documents.
    • Then, the document talks about two alternatives to launch threads if we want to return something (callables), or if we don’t want to block main thread waiting for other threads to finish (completable futures). Exercises 1 and 2 let you practice these concepts. Also, you can download the source code of the examples provided in this section to try them.
    • Next section is optional for this module, and it talks about how to use Lock interface and some subtypes of it, such as ReadWriteLock to easily synchronize object access, even if we want to have two types of threads over this object: one for reading values and another one for writing or updating values. You can do Exercises 3 and 4 to practice with this.
    • Finally, there’s a section to read about the Fork/Join framework, which let us divide a complex task among multiple threads (fork) and wait for them to finish and show the results (join). You can do Exercise 5 of this subsection.

Threads and shared data

Estimated time: 2 hours

  • In this document, you will see several strategies for handling shared data in threads, depending on whether you want to manage simple values, arrays, or collections. You will learn how to use atomic variables for simple data, as well as how to use special thread-safe collection types. Additionally, you will learn the difference between synchronized and concurrent collections, an essential concept in terms of performance. You can complete Exercises 1 and 2.

3. Threads in JavaFX applications

In this part, we are going to learn how to use threads in JavaFX applications, as there are certain aspects we need to consider that were not necessary in console applications.

Estimated time: 3 hours

  • In this document, you will find the contents of this session:
    • First, the problem is introduced: we cannot access graphical elements, such as text fields or labels, from secondary threads in JavaFX — only from the main JavaFX application thread.
    • Next, we explore how to solve this issue using the Platform.runLater method, which allows secondary threads to delegate tasks (such as updating UI controls) to the main thread. This approach is suitable if your application does not require advanced thread management. You can complete Exercise 1 to practice this technique.
    • Then, the document covers the JavaFX concurrency framework and the various interfaces and classes it provides for handling threads in a more robust and professional way. You can use Service instances to launch background tasks and respond to events based on whether the task completed successfully, was cancelled, or failed. To gain hands-on experience with different types of services — such as simple services and scheduled services — you can complete Exercises 2 and 3.