From today, I will start a series of blogs introducing topics related to multithreading in Java. It will only focus on entry contents rather than in-depth complicated issues.
In this blog I will disucss the following topics:
- Process and Thread
- State of thread
- Implement multithreading in Java
- Thread
- Runnable
- The difference between start() and run() in Thread
Process and Thread
Let’s first look at the difference between processes
and threads
in the operating system:
- Process: Each process has its own code and data space (process context). Switching between processes will have a large overhead. A process contains 1–n threads. (Process is the smallest unit of resource allocation)
- Thread: The same type of thread shares code and data space, each thread has an independent running stack and program counter (PC), and the thread switching overhead is small. (Thread is the smallest unit of CPU scheduling)
Multi-process
means that the operating system can run multiple tasks (programs) at the same time.
Multithreading
refers to the execution of multiple sequential streams in the same program.
State of thread
Thread includes the following 5 states.
- New: after the thread object is created, it enters the
new
state. For example,Thread thread = new Thread()
. - Runnable: also known as “executable state”. After the thread object is created, other threads call the object’s
start()
method to start the thread. For example,thread.start()
. Threads in therunnable
state may be scheduled for execution by the CPU at any time. - Running: thread obtains CPU permission to execute. It should be noted that the
thread can only enter the running state from the runnable state
. - Blocked: the thread gives up the CPU usage right for some reasons and temporarily stops running. Until the thread enters the
runnable
state, there is no chance to go to therunning
state. There are three blocking situations:- Waiting blocking - by calling the thread’s
wait()
method, the thread is allowed to wait for the completion of a job. - Synchronous blocking - the thread fails to acquire the synchronized lock (because the lock is occupied by other threads), it will enter the synchronized blocking state.
- Other blocking - by calling the thread’s
sleep()
orjoin()
or issuing an I/O request, the thread will enter the blocking state. When thesleep()
state times out,join()
waits for the thread to terminate or time out, or the I/O processing is complete, the thread re-enters therunnable
state.
- Waiting blocking - by calling the thread’s
- Dead: the thread finishes execution or exits the
run()
method due to an exception, the thread ends its life cycle.
Implement multithreading in Java
To implement multithreading in Java, there are two methods: one is to inherit the Thread class
, and the other is to implement the Runable interface
. (Actually, there should be three, and one is to implement the Callable interface
and use with Future
and Thread pool
, this blog does not talk about this)
Introduction to Thread and Runnable
Runnable
is an interface that contains only one run()
method:
1 | public interface Runnable { |
Thread
is a class. Thread
itself implements the Runnable
interface:
1 | public class Thread implements Runnable {} |
Comparing Thread and Runnable
Thread
and Runnable
are both used to implement multithread.
Difference:
Thread
is a class, andRunnable
is an interface. We know that “a class can only have one parent class, but it can implement multiple interfaces”, soRunnable
has better scalability.Runnable
can be used for “resource sharing”. That is, multiple threads are based on a certainRunnable
object, they will share the resources on theRunnable
object.
Generally, it is recommended to implement multithreading through Runnable
Runnable
is suitable for multiple threads of the same program code to process the same resource- The limitation of single inheritance in Java can be avoided by using
Runnable
- Using
Runnable
creases the robustness of the program, the code can be shared by multiple threads. The code and data are independent - The thread pool can only be put into the thread that implements
Runable
orCallable
, not directly into the class that inheritsThread
Thread example
1 | class MyThread extends Thread{ |
Results:
1 | Thread-0 Selling ticket: ticket10 |
MyThread
inherits from Thread
, it is a custom thread. Each MyThread
will sell 10 tickets.
The main thread creates and starts three MyThread
sub-threads. Each child thread sold 10 tickets.
Runnable example
Next, we modify the above program: implement Runnable
interface to achieve multithreading
1 | class MyThread implements Runnable { |
Results:
1 | Thread-0 Selling ticket: ticket10 |
Unlike the above “MyThread
inherits from Thread
“, MyThread
here implements the Runnable
interface.
The main thread creates and starts 3 child threads, and these 3 child threads are all created based on mt
. The running result is that these 3 sub-threads sold a total of 10 tickets. This shows that they share the MyThread
interface.
Note that this example could sell more than 10 tickets! T1
, t2
, and t3
share a task and operate on the ticket at the same time. It may cause concurrency problems. For example, the value of the tickets read at the beginning of t1
and t2
are both 10
, and after t1
sells a ticket, ticket = 9
; at this time t2
sells tickets, and the number of tickets read before t2
is 10
, so, after t2
sells, it is also 9
tickets. In this case, the problem arises: after the original 10
tickets, t1
and t2
each sell one ticket, but there are 9
tickets left! This leads to “the actual number of tickets sold may be more than 10”! We will discuss how to avoid this in the future blogs.
The difference between start() and run() in Thread
- start(): start a new thread, and the new thread will execute the corresponding
run()
method.start()
cannot be called repeatedly- After the
start()
method is called, the multi-threaded code is not executed immediately, but the thread is made to beRunnable
. When to run the code is determined by the operating system - If the
start()
method is called repeatedly, ajava.lang.IllegalThreadStateException
exception will occur
- After the
- run():
run()
is just like the normal member method and can be called repeatedly. Callingrun()
alone will executerun()
in the current thread without starting a new thread!
start() and run() example
1 | public class StartAndRun { |
Results:
1 | main call myThread.run() |
Thread.currentThread().getName()
is used to get the name of “current thread”. The current thread refers to the thread that is being scheduled for execution in the CPU.
myThread.run()
is called in “main thread”, and the run()
method runs directly on “main thread”.
myThread.start()
will start “thread myThread”. After “thread myThread” starts, it will call the run()
method; the run()
method at this time is running on “thread myThread”.
Source code of start() and run() (based on JDK 11.0.5)
The source code of the start()
method in Thread.java
is as follows:
1 | public synchronized void start() { |
Note: start()
actually starts the thread through the native method start0()
. And start0()
will run a new thread, and the new thread will call the run()
method.
1 | private native void start0(); |
The source code of the run()
method in Thread.java
is as follows:
1 | public void run() { |
target
is a Runnable
object. run()
will directly call the run()
method of the Runnable
member of the Thread
, and does not create a new thread.