I. The basic of Java Thread
According to Java docs, here is the definition of Thread
A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.
Actually, there is a direct mapping between a JVM thread and an OS thread. Every thread has its own priority. The more prioritized, the earlier thread’s execution be performed. We also can set priority to the thread by using thread.setPriority(priority)
note that priority
is in [1, 10]
Thread mainly have 5 state
- Created: The thread’s instance is created
- Runnable: Thread ready for running
- Running: Thread is running
- Blocked: Thread is in the blocked state.
- Terminated: Thread finishes its execution.
So how to make a thread? There are two ways to create a thread
- Extend Thread.
We can create a thread by extending the Java Thread and override the run()
function
To start the thread we call the start()
function
2. Using Runnable
Make an implementation for the Runnable
interface and then create a thread by using the default Thread
constructor with Runnable
II. Thread’s common methods
- start(): Cause the thread to begin execution
- join(): When the thread join is performed, the calling thread will go into blocked state, it remains in this state until the reference thread complete.
3. sleep(): this is a static function which causes the current thread to suspend execution for a specified period. Sleeping current thread make more time available for the processor to do other execution with other threads of current application or other application. In the above example, the thread t1
slept in 5 seconds.
4. interrupt(): this function sends a signal to a thread that it should stop what it is doing and do something else. We can decide exactly how a thread responds to an interrupt, but normally thread will be terminated.
III. Thread Interference
There is one java multiple thread question that I sometimes ask the candidate in the interview.
What output this below program will print?
Can you guess? Yes, the answer is they can print 0, 1 and -1.
But why does it happen? That is because of interferences. Interference when two or more operations running in different threads but acting in same data, interleave. You can imagine that each operation has it own sequences of steps but it’s sequences of steps overlap with other operation’s sequences.
To understand the output of the above program, let understand the operation count ++
, it include 3 steps:
- Get the value of
count
- Increment the retrieved value by 1
- Store value back to
count
For count --
the steps are the same. But instead of incrementing the retrieved value, it decrements the retrieved value.
So if the processor execute the below order of steps, we will get output is -1
t1
retrievecount
t2
retrievecount
t1
increments retrieved value (retrieved value is 1 now)t2
decrements retrieved value (retrieved value is -1 now)t1
store retrieved value tocount
(count is 1 now)t2
store retrieved value tocount
(count is -1 now)
With different orders, we can have different results. So, how can we avoid this situation?
IV. Thread safety, multithreading.
Thread safety in java is the process to make program safe when using multi thread. How to achieve that, how can we avoid some unexpectes state? There are different ways to archive:
- Using Java synchronized keyword
JVM guarantees that when we use thesynchronized
keyword, the code will be executed by only one thread at a time. Code example:
The result of the above code is:
Thread[Thread-0,5,main] counted 1
Thread[Thread-0,5,main] counted 2
Thread[Thread-0,5,main] counted 3
Thread[Thread-1,5,main] counted 1
Thread[Thread-1,5,main] counted 2
Thread[Thread-1,5,main] counted 3
When one thread enter to the synchronized
code block of an object, it will acquire look on that object. That make other threads which try to enter that synchronized code block will be blocked. And the lock only releases when the code execution is finished.
There is another way when using synchronized
, that is using synchronized
method, like the below code:
The result is exactly the same, the difference is when we use synchronized
function, it will lock the object. Here is the object is asyncDemo
.
2. Using ReetranLock
The idea is simple, when the thread calls countTo
function, firstly, it will try to acquire the lock
. If the lock is acquired by other thread, the current thread will wait until lock
release.
3. Using atomic
Go back to our example of increment and decrement value. We can avoid it by using atomicInteger
The result is always 0. That is because now the code run in sequences. But why we need sleep before showing the result. Because we want to guarantee that the execution of both functions will be done before we display.
Java also provides other types of Atomic include: AtomicLong
, AtomicBoolean
, AtomicReference
.
4. Using Volatile
When using volatile
, the thread will understand that it can only use the main memory, then instead of retrieving then change and put back to main memory, the thread now will do execution in the main memory, so the count
value is guaranteed that it is always up-to-date.