/ Java  

Java Multithreading 1: Basic Concepts

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.

  1. New: after the thread object is created, it enters the new state. For example, Thread thread = new Thread().
  2. 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 the runnable state may be scheduled for execution by the CPU at any time.
  3. Running: thread obtains CPU permission to execute. It should be noted that the thread can only enter the running state from the runnable state.
  4. 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 the running state. There are three blocking situations:
    1. Waiting blocking - by calling the thread’s wait() method, the thread is allowed to wait for the completion of a job.
    2. 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.
    3. Other blocking - by calling the thread’s sleep() or join() or issuing an I/O request, the thread will enter the blocking state. When the sleep() state times out, join() waits for the thread to terminate or time out, or the I/O processing is complete, the thread re-enters the runnable state.
  5. 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
2
3
public interface Runnable {
public abstract void run();
}

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, and Runnable is an interface. We know that “a class can only have one parent class, but it can implement multiple interfaces”, so Runnable has better scalability.
  • Runnable can be used for “resource sharing”. That is, multiple threads are based on a certain Runnable object, they will share the resources on the Runnable 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 or Callable, not directly into the class that inherits Thread

Thread example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyThread extends Thread{  
private int ticket = 10;

public void run(){
for(int i = 0; i < 20; i++){
if(this.ticket > 0){
System.out.println(this.getName()+" Selling ticket: ticket" + this.ticket--);
}
}
}
};

public class ThreadTest {
public static void main(String[] args) {
// Create 3 threads t1, t2, t3: every thread sell 10 tickets
MyThread t1=new MyThread();
MyThread t2=new MyThread();
MyThread t3=new MyThread();

t1.start();
t2.start();
t3.start();
}
}

Results:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Thread-0 Selling ticket: ticket10
Thread-1 Selling ticket: ticket10
Thread-2 Selling ticket: ticket10
Thread-1 Selling ticket: ticket9
Thread-0 Selling ticket: ticket9
Thread-1 Selling ticket: ticket8
Thread-2 Selling ticket: ticket9
Thread-1 Selling ticket: ticket7
Thread-0 Selling ticket: ticket8
Thread-1 Selling ticket: ticket6
Thread-2 Selling ticket: ticket8
Thread-1 Selling ticket: ticket5
Thread-0 Selling ticket: ticket7
Thread-1 Selling ticket: ticket4
Thread-2 Selling ticket: ticket7
Thread-1 Selling ticket: ticket3
Thread-0 Selling ticket: ticket6
Thread-1 Selling ticket: ticket2
Thread-2 Selling ticket: ticket6
Thread-2 Selling ticket: ticket5
Thread-2 Selling ticket: ticket4
Thread-1 Selling ticket: ticket1
Thread-0 Selling ticket: ticket5
Thread-2 Selling ticket: ticket3
Thread-0 Selling ticket: ticket4
Thread-2 Selling ticket: ticket2
Thread-0 Selling ticket: ticket3
Thread-2 Selling ticket: ticket1
Thread-0 Selling ticket: ticket2
Thread-0 Selling ticket: ticket1

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyThread implements Runnable {  
private int ticket = 10;

public void run(){
for(int i = 0; i < 20; i++){
if(this.ticket > 0){
System.out.println(Thread.currentThread().getName()+" Selling ticket: ticket" + this.ticket--);
}
}
}
};

public class RunnableTest {
public static void main(String[] args) {
MyThread mt = new MyThread();

Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);

t1.start();
t2.start();
t3.start();
}
}

Results:

1
2
3
4
5
6
7
8
9
10
Thread-0 Selling ticket: ticket10
Thread-2 Selling ticket: ticket8
Thread-1 Selling ticket: ticket9
Thread-2 Selling ticket: ticket6
Thread-0 Selling ticket: ticket7
Thread-2 Selling ticket: ticket4
Thread-1 Selling ticket: ticket5
Thread-2 Selling ticket: ticket2
Thread-0 Selling ticket: ticket3
Thread-1 Selling ticket: ticket1

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 be Runnable. When to run the code is determined by the operating system
    • If the start() method is called repeatedly, a java.lang.IllegalThreadStateException exception will occur
  • run(): run() is just like the normal member method and can be called repeatedly. Calling run() alone will execute run() in the current thread without starting a new thread!

start() and run() example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class StartAndRun {
public static void main(String[] args) {
Thread myThread=new MyThread("myThread");

System.out.println(Thread.currentThread().getName()+" call myThread.run()");
myThread.run();

System.out.println(Thread.currentThread().getName()+" call myThread.start()");
myThread.start();
}
}

class MyThread extends Thread{
public MyThread(String name) {
super(name);
}

public void run(){
System.out.println(Thread.currentThread().getName()+" is running");
}
}

Results:

1
2
3
4
main call myThread.run()
main is running
main call myThread.start()
myThread is running

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public synchronized void start() {
// If the thread is not in the "Runnable" state, an exception is thrown!
if (threadStatus != 0)
throw new IllegalThreadStateException();

// Add the thread to ThreadGroup
group.add(this);

boolean started = false;
try {
// Start the thread via start0()
start0();
// Set the started variable
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}

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
2
3
4
5
public void run() {
if (target != null) {
target.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.