Java Concurrency, Part 1: Threads
Learn more about Threads and concurrent programming in this series on Java concurrency.
Join the DZone community and get the full member experience.
Join For FreeConcurrency is a game-changer for building Java applications, referring to the ability to run several programs at the same time using multiple threads.
This post is the first in a series of posts about Java concurrency. All code shared in this article has been tested in Java 12.
So without further ado, let's get started.
You may also like: [DZone Refcard] Core Java Concurrency
A thread, also called the ''Lightweight Process,'' is treatment unity. Threads execute code in parallel with other threads currently running. When you've only got one processor, there is a thread running at the same time as the others, and then, you only have the impression of concurrency (I don't say it's not useful, I say it's different).
However, when you've got multiple processors, you'll see the power of multithreading. In this case, you can have your threads distributed on different processors of the computer.
In Java, a thread is an instance of the class java.lang.Thread
. A Thread
can be managed in one of the following two ways:
- Directly mapped to a native thread of the operating system. This is used when the operating system provides a preemptive threading system.
- Managed by the virtual machine in a preemptive way.
A preemptive system is a system in which the threads are managed by a scheduler and can be interrupted at any time to give the processor over to another thread. When you program, you don't have to pay attention to which type of thread you use, the result will normally be the same. But you have to know that there can be differences between the operating systems.
There are three very important concepts when implementing concurrent programming:
- Atomicity: An operation is said to be atomic when it cannot be interrupted. There are almost no atomic operations in Java; the only one we have is the assignation a = 5, but a = b++ is not atomic. In some cases, you'll have to make atomic some actions with synchronization. We'll see later how to do that.
- Visibility: This occurs when a thread must watch the actions of another thread, for example, the termination of the thread. This also implies some kind of synchronization.
- Order of execution: When you have a normal program, all your lines of code run in the same order every time you launch the application. This is not the case when you make concurrent programming. Your first instruction can follow an instruction from thread B or by the first instruction. And that can change every time you launch the application. The order of execution is not guaranteed! I will certainly repeat that sometimes, but either way, that's important to know.
We'll see these concepts more deeply in the other parts of the set.
For now, let's start introducing the Thread
class in Java. You can create threads in two ways:
- Extends
Thread
- Implements
Runnable
and passes an instance of your news class to theThread
constructor
The first solution isn't a good solution because what you're creating is not a newly specialized thread, but we do get several instructions to run in a new Thread, namely a Runnable
. Implementing Runnable
is also better because Runnable
is an interface, and so, you can also extend a class, and implementing Runnable
, in some cases, proves to be very useful.
In my examples, I'll always use the second option. So, let's declare our first Runnable
:
public class MyFirstRunnable implements Runnable{
@Override
public void run() {
System.out.println("In a thread");
}
}
And then, let's use it to create a new Thread
and start it:
Thread thread = new Thread(new MyFirstRunnable());
thread.start();
The Thread will be stopped when the end of the run()
is reached. You cannot force a thread to stop (there is a stop()
method, but it's deprecated). We'll see later how to properly stop a thread.
And now, what happens if we add a simple line of code to our program? Let's find out:
Thread thread = new Thread(new MyFirstRunnable());
thread.start();
System.out.println("In the main Thread");
Can you predict the result of this code? Nobody can; it's not predictable. For example, you can have:
In a thread
In the main Thread
Or:
In the main Thread
In a thread
And we cannot do better than that.
You can use the Runnable
several times:
Runnable runnable = new MyFirstRunnable();
for(int i = 0; i < 25; i++){
new Thread(runnable).start();
}
Now, 25 threads are launched.
You can also give names to Thread
using the setName()
method. You can get the name of the current Thread using Thread.currentThread().getName()
. Let's do a little example :
public class MySecondRunnable implements Runnable{
@Override
public void run() {
System.out.printf("I'm running in thread %s \n", Thread.currentThread().getName());
}
}
Runnable runnable = new MySecondRunnable();
for(int i = 0; i < 25; i++){
Thread thread = new Thread(runnable);
thread.setName("Thread " + i);
thread.start();
}
This is the best example to see that the other is unpredictable. Here are two executions on my machine:
I'm running in thread Thread 0
I'm running in thread Thread 1
I'm running in thread Thread 2
I'm running in thread Thread 3
I'm running in thread Thread 4
I'm running in thread Thread 5
I'm running in thread Thread 7
I'm running in thread Thread 14
I'm running in thread Thread 13
I'm running in thread Thread 12
I'm running in thread Thread 11
I'm running in thread Thread 10
I'm running in thread Thread 9
I'm running in thread Thread 8
I'm running in thread Thread 6
I'm running in thread Thread 15
I'm running in thread Thread 16
I'm running in thread Thread 17
I'm running in thread Thread 18
I'm running in thread Thread 19
I'm running in thread Thread 20
I'm running in thread Thread 21
I'm running in thread Thread 22
I'm running in thread Thread 23
I'm running in thread Thread 24
I'm running in thread Thread 0
I'm running in thread Thread 1
I'm running in thread Thread 2
I'm running in thread Thread 3
I'm running in thread Thread 4
I'm running in thread Thread 5
I'm running in thread Thread 6
I'm running in thread Thread 7
I'm running in thread Thread 8
I'm running in thread Thread 9
I'm running in thread Thread 10
I'm running in thread Thread 11
I'm running in thread Thread 12
I'm running in thread Thread 13
I'm running in thread Thread 14
I'm running in thread Thread 15
I'm running in thread Thread 16
I'm running in thread Thread 17
I'm running in thread Thread 18
I'm running in thread Thread 19
I'm running in thread Thread 20
I'm running in thread Thread 21
I'm running in thread Thread 22
I'm running in thread Thread 23
I'm running in thread Thread 24
As you can see, the order in which the threads' instructions are executed is not guaranteed at all.
So, we are done with the first part of this suite of articles on Java Concurrency. In the next post, we'll explore some of the operations you can make directly on threads (for example, stopping, joining, sleeping, etc.).
Stay tuned!
The sources of this post are available here: Java Concurrency Sources Part 1.
Further Reading
[DZone Refcard] Core Java Concurrency
Published at DZone with permission of Baptiste Wicht, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Boosting Application Performance With MicroStream and Redis Integration
-
Implementing RBAC in Quarkus
-
Test Data Tutorial: A Comprehensive Guide With Examples and Best Practices
-
Data Freshness: Definition, Alerts To Use, and Other Best Practices
Comments