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
newstate. 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 therunnablestate 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
runnablestate, there is no chance to go to therunningstate. 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 therunnablestate.
- 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:
Threadis a class, andRunnableis an interface. We know that “a class can only have one parent class, but it can implement multiple interfaces”, soRunnablehas better scalability.Runnablecan be used for “resource sharing”. That is, multiple threads are based on a certainRunnableobject, they will share the resources on theRunnableobject.
Generally, it is recommended to implement multithreading through Runnable
Runnableis 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
Runnablecreases 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
RunableorCallable, 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.IllegalThreadStateExceptionexception 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.