The Concurrency Breakdown
An almost brief look into concurrent programming.
Concurrency is defined as when two or more events occur at the same time. In programming, concurrency is defined when a system executes more than one task or process at a time. This can be achieved with a single core or Central Processing Unit(CPU) or multiple.
An example of concurrent tasks can be when you are using a reading app or writing a word document and at the same time you're streaming music. The computer is not only managing the tasks of updating the reading app but it also managing the music app where it's constantly updating either apps to keep up with changes made such as playing the next track, updating display, or navigating playlists whilst simultaneously keeping up with the keyboard and mouse and listening for any commands to update document and formatting as you provide input. All these tasks seem to being carried out simultaneously.
However, concurrent tasks do not actually execute at the same time. What happens is that the operating system switches back and forth at such a rapid rate that it seems as though the tasks are being carried out simultaneously.
Concurrent Programming has 2 basic units of execution:
-Processes
-Threads
We can define a process as instance of an application, with it's own memory address space and all the resources it requires to be executed. An operating system can have multiple processes and due to the separate memory of these processes, each can be executed independently and without affecting other processes in the system.
A thread can also be described as a process, however, threads can be used for smaller 'lightweight' tasks and unlike processes they cannot be composed of several threads but they can interact with them. Therefore, this is the reason why threads are referred to as lightweight processes. Threads within a single process share the same memory address space as the process and this enables communication between the threads meaning they are able to share varying functionality within the process.
Inter-process communication(IPC) refers to communication between processes and most operating systems support resources for this communication. IPC can also support communication of processes between different operating systems.
Processes and threads both provide an execution in environment, however, due to their lightweight nature, threads require lesser resources than processes to execute.
How do you create a thread in Java and run it?
There are two ways of creating a thread in Java:
- Extending the thread class
- Implementing the runnable interface
Both methods achieve the same results, however, in java it is not possible to have multiple inheritance classes (i.e we cannot extend our thread class to a class that's already inheriting another class) therefore, if we used the first method and extend the thread class we would not be able to extend any other base classes. However, if we implement the runnable interface our classes can extend other base classes.
Extending the thread class
Using the first method, by extending the thread class firstly we need to override run() method available in Thread class which is available in the java.lang packagae. Once once Thread object has been created we can then start it by calling start() method, in our main(TestThread) thread, which executes a call to run() method.
public class Extendedthread extends Thread{
@Override
public void run(){
system.out.println("This is the extended thread");
}
}
public class TestThread {
public static void main(String[] args) {
Thread t = new Extendedthread();
t.start();
}
}
Implementing the thread class
Using the second method, by implementing the runnable interface, firstly we need to implement a run() method provided by a Runnable interface then secondly, we'll instantiate a thread object t. Once object t is created, we can start it by calling start() method,in our main(TestThread) thread, which executes the call to run() method
public class RunnableThread implements Runnable{
@Override
public void run() {
System.out.println(" This is the runnable thread ");
}
}
public class TestThread {
public static void main(String[] args) {
Runnable r = new Runnablethread();
Thread t = new Thread(r);
t.start();
}
}
What are the different states of a thread and when do the state transitions occur?
New − A new thread begins its life cycle in the new state as soon as it is created and it remains in this state until the program starts the thread.
Runnable − Once the new thread is started, it becomes runnable and considered to be executing its task in this state, (a runnable state).
Blocked - The runnable thread transitions to a blocked state when it attempts to perform a task that cannot be completed instantly and has to temporarily wait until that task completes.
Waiting − A thread can transition into a waiting state due to various factors. Although, usually the runnable thread transitions to a waiting state as it waits for another thread to finish performing a task. It only transitions back to the runnable state when another thread sends a signal to the waiting thread to continue executing.
Timed Waiting − A runnable thread transitions to the timed waiting state for a specified interval of time. A java thread can be placed in this state calling it’s sleep(long millis) method or wait(long millis) method. It only transitions back to the runnable state when that time interval expires or when the event it is waiting for takes place.
Terminated/Dead − A runnable thread enters the terminated state when it completes successfully its task or otherwise terminates due to error or forced stop.
What is a daemon thread and what are its use cases?
Daemon threads are considered low priority threads that are created by the JVM and run in the background performing basic services for programs like garbage collection etc. The JVM does not wait for these threads to finish execution in order to terminate a program. It will exit as soon as all user threads(non-daemon threads) finish execution.
How do you create a daemon thread?
In java, the java.lang.Thread class contains a method called setDaemon(boolean) which can be used to convert a user thread to a daemon thread. The boolean argument placed in the method's parameters is set to 'true' to turn a thread to a daemon thread. The thread class also contains a method isDaemon() which can be used to check whether a thread is a daemon thread or not.
here's a code example:
public class ExampleThread {
public static void main(String[] args) {
Runnable r = new RunnableThread();
Thread t = new Thread(r);
t.setDaemon(true);
t.start();
}
}
What is Java Memory Model (JMM)?
Java Memory Model is a memory model that defines the interactive behaviors in multi-threaded Java processes with respect to the shared memory. While it acts on the data synchronization process between the working memory and the main memory, it sets out how and when to do data synchronization.
The JMM specifies how the Java virtual machine works with the computer’s memory, and it divides the JVM memory into two areas.
- Heap
- Stack
Where the heap is where all the objects are stored and the stacks are where the threads do their work. Every running thread has its own stack, storing details about the methods and field variables that are created or called to run the thread. Field variables in a single stack are not accessible to the other threads, so each thread creates the field variables that are stored whether or not the same variables are referenced in other threads. The heap stores information regarding all objects that have been created in the Java program.
What are deadlock, livelock, and starvation? What causes these conditions?
JVM and most OS have thread schedulers for executing tasks without conflict, they are very effective for managing concurrent tasks, but at times threads can cause problems such as livelock, starvation or deadlock. These can result from a waiting thread not being able proceed with it's task because it’s waiting (either directly or indirectly) for another thread to proceed, while simultaneously the other thread cannot proceed because it’s also waiting for the waiting thread to proceed.
Deadlock results when two or more tasks become blocked because each thread is waiting for a shared resource that is held by another thread. This can be a consequence of synchronization problems in the process, if for example different threads need particular resources at opposite times, which can cause a block if the timing of the synchronization is wrong.
A livelock results when there are two tasks running in a process in which one task's state changes as a response to the other task's state changing. Consequently these tasks are caught in a loop of state changes and unable to continue with their tasks beause the keep sending information back and forth.
Starvation occurs when you have a task in your system that never gets a resource that it needs to continue with its execution. When there is more than one task waiting for a resource and the resource is released, the system has to choose the next task that can use it. If your system doesn't have a good algorithm, it can have threads that are waiting for a long time for the resource.
What happens if you don’t override the thread class run() method?
Absolutely nothing! If the run() method is not overridden to apply to the newly created thread,
the thread will not be called on, and therefore it will not execute or produce any output.
This is because, the Java 'Thread' class’s run() method doesn’t contain any tasks in its body
so if we did not override the run() method, the Thread class’s run() method will be called and
that is why nothing happens.
What is atomic operation and what are atomic classes in the Java Concurrency API?
An atomic operation is a kind of operation that occurs independently to the rest of the tasks in a process and without interference from other processes. An atomic variable is a kind of variable that has atomic operations to set and get its value.
They can be implemented using a synchronization mechanism or in a lock-free manner that doesn't need synchronization. Examples of concurrency API include AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference which can be found in Java's atomic class.
What are Executor and ExecutorService and what are the differences between them?
An executor is a class that allows programmers to execute concurrent tasks without being worried about the creation and management of threads. It provides factory methods for creating different types of thread pools. which contain a group of worker threads previously created and are called on to execute current tasks in the process. The ExecutorService is an interface that inherits from the Executor interface class. It provides various methods for running and closing and submitting thread pools at different times.
What are Concurrent Collection Classes?
The Concurrent Collections Classes in Java, refers to a package called java.util.concurrent, that contains interfaces and implementation classes that enable developers to write better concurrent programs. This package includes some addittions to the Java Collections Framework and these are called Java Concurrent Collections. Some the interfaces in the class include: BlockingQueue, ArrayBlockingQueue, SynchronousQueue, PriorityBlockingQueue, LinkedBlockingQueue, DelayQueue, BlockingDeque, ConcurrentHashMap, ConcurrentSkilListMap, CopyOnWriterArrayList and more. These classes all perform different functions that support and improve the ability to perform concurrent programming.
Conclusion
At this point, you should have a better understanding of concurrent programming. You should know the difference between threads and processes and how to create threads in java. You should know the different transition states of a thread the problems that could occur with synchronization as well as the tools that support concurrent programming.
Hope this article was helpful. Till next time!
References:
Saifi I. 2018, How to create threads and run in Java?, viewed 26 0ctober 2021, https://www.loginworks.com/blogs/create-threads-run-java/
HowToDoInJava 2020, Java Thread Life Cycle and Thread States, viewed 26 October 2021, https://howtodoinjava.com/java/multi-threading/java-thread-life-cycle-and-thread-states/
Singh C. n.d, Daemon thread in Java with example, viewed 26 October 2021, https://beginnersbook.com/2015/01/daemon-thread-in-java-with-example/
Joe 2018, Java Concurrent Collections, viewed 26 October 2021, https://javapapers.com/java/java-concurrent-collections/avapapers.com/java/java-concurrent-collections/
Open Genus n.d, Using Threads in Java, viewed 26 October 2021, https://iq.opengenus.org/thread-java/