This article is very basic and suitable for beginners.

Threads and processes

From the level of operating system, process is the dynamic execution process of application program, process represents the execution of a program. Processes are also the basic unit of operating system resource management and scheduling. Threads are a further division of processes, and a process can have multiple threads. A thread is the smallest unit of program execution.

Differences and relationships:

  • Process is the smallest unit of operating system allocation of resources and scheduling, thread is the smallest unit of program execution
  • A process consists of one or more threads that correspond to a path of execution for a program
  • Processes are independent of each other and each process has its own memory space
  • Multiple threads sharing process space
  • Thread context switches faster than processes

Threads and processes in Java

When you run a Java program, the system generates a Java process. Those of you who know JVMS know that each Java process has its own memory area, including the heap, method area, virtual machine stack, and so on.

Threads in Java processes have their own virtual stack outside of areas such as the shared heap. That is, threads in Java have their own separate space in addition to sharing process memory.

Use a multithreaded approach

1. Inherit Thread and override run

class MyThread extends Thread{
	@Override
	public void run(a){
		// Thread executes code}}Copy the code

Use threads by inheriting the Thread class and overriding the run method. Once the class is written, you simply create an instance object of the class and call the start() method to start the thread.

MyThread t = new MyThread();
t.start();
Copy the code

2. Implement Runnable interface

class MyRunnable implements Runnable{
	@Override
	public void run(a){
        // Thread executes code}}Copy the code
Thread t = new Thread(new MyRunnable());
t.start();
Copy the code

Use threads by implementing the Runnable interface and overriding the run() method.

Because the Runnable interface has only one method and is annotated @functionalInterface, it can be simplified with lambda expressions.

Thread t = new Thread(()->{
	// Thread executes code
});
t.start();
Copy the code

3. Implement Callable interface

The run method of the Runnable interface does not return a value, which is not applicable in some scenarios. So you can use Callable to create threads that have data to recycle.

FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
	@Override
	public Integer call(a) throws Exception {
		// Thread execution
		return 2; }}); Thread t =new Thread(task);
t.start();

// The thread is blocked here before execution ends
System.out.println(task.get());
Copy the code

You can create a thread with a return value by implementing the Callable interface and creating a FutureTask object.

** Note: ** Calling FutureTask’s GET method before the thread terminates causes the caller to block until the thread terminates and returns the result.

4. Use thread pools

Both creating and releasing threads are expensive, and too many threads reduce code readability and make it unmanageable. Thread pools are a good solution to these problems.

Multiple threads can be maintained in a thread pool. When a task is submitted to the thread pool, the thread pool automatically allocates threads for execution. These threads do not die when the task is completed, which avoids frequent thread creation and destruction, and is easier to manage because all threads are in a pool.

Creating a thread pool

// Single thread pool, only one thread
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// Fixed size thread pool, specifying the number of threads
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
// Cache thread pool, no core thread, each time a new thread is created to execute the task
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
Copy the code

The three simplest ways to create a FixedThreadPool are covered here for the moment, and the most obvious is FixedThreadPool.

In alibaba’s Java coding specification, try not to use the Executors class to create threads and use ThreadPoolExecutor instead. I won’t cover ThreadPoolExecutor here, but I’ll cover it in more detail in another article, along with the various thread pools.

Submit a task

// Runnable
fixedThreadPool.submit(new Runnable() {
	@Override
	public void run(a) {
		// Thread executes code}});// Callable
Future<Integer> future = fixedThreadPool.submit(new Callable<Integer>() {
	@Override
	public Integer call(a) throws Exception {
		// Thread executes code
		return null; }}); System.out.println(future.get());Copy the code

Similar to creating a thread directly, there are two types of submitting tasks to a thread pool: return and no return. Implement Callable and Runnable interfaces respectively.

When using Callable, note that the Submit method returns a Future object whose get method retrieves the thread’s return. However, as with FutureTask, if the thread does not complete, the caller of GET will block.

Thread state

As shown above, Java threads are in six states: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED.

Each of these states and the transitions between them are described next.

The NEW NEW

The new state is the initial state of a Thread that will enter after a new Thread.

The executable RUNNABLE

This state indicates that the thread can execute, that is, the CPU can schedule the thread. The thread enters this state after calling thread’s start method.

The RUNNABLE state contains two states, RUNNING and READY. The RUNNING state indicates that the thread is executing, and the READY state indicates that the thread is executing and waiting for CPU scheduling.

The transition between the two states is as follows (*) :

  • READY->RUNNING: The CPU schedules the thread and the thread gets a time slice to start execution
  • RUNNING->READY: the time slice of the thread ends or yields itself

Blocking the BLOCKED

The status that a thread enters after it fails to compete for a lock, and that synchronized fails to compete for a lock.

WAITING for WAITING

The wait state is different from the blocking state, which is caused by the failure of the contention lock, whereas the wait is actively entered. For example, methods such as wait(), park(), join() are called to stop the thread.

BLOCKED vs. WAITING

  • BLOCKED is caused by lock contention failures, and WAITING is often the result of actively calling related methods
  • BLOCKED ends when the lock is released, and WAITING often requires specific operations such as notify(), notifyAll(), unpark(), and so on

Time waiting TIMED_WAITING

Timed wait is a time out mechanism added to WAITING. In addition to notify, it can exit when the timer reaches a certain time.

Common TIMED_WAITING include sleep(), wait(long timeout), park(long timeout), and Join (long timeout)

Termination of the TERMINATED

A thread enters the terminating state when its run method completes, or when the main thread terminates.

Thread state transition

Frequently seen exam

**Q: ** The difference between sleep and wait

  1. Calls are different: Sleep is called using the Thread class, and wait is called using the lock object in a synchronized block.
  2. The effect is different: the sleep method puts the thread into a wait state, but does not release the acquired lock. Wait causes the thread to wait and releases the lock.
  3. Wake up in different ways: Sleep wakes up automatically after a timeout, and wait wakes up the thread holding the lock with notify or notifyAll.
  4. Different states: sleep causes the thread to be in TIMED_WAITING state, while wait without arguments causes the thread to be in WAITING state.