[Java] Java multi-threaded programming foundation

1. Process and thread

1.1 Basic understanding of processes and threads

1.1.1 Process

A process is a dynamic execution process of a program, which has gone through a complete process from code loading, execution, to execution completion; it is also the basic unit for concurrently executing programs to allocate and manage resources during execution, and the basic unit for competing for computer system resources unit.

1.1.2 Threads

A thread can be understood as a program fragment executed in a process. It is an execution unit of a process, a schedulable entity in a process, and a basic unit that is smaller than a process and runs independently. A thread is also called a lightweight process.

1.2 Why there are threads

Each process has its own address space, that is, the process space. Under the network or multi-user switching, a server usually needs to receive a large number of concurrent requests from an uncertain number of users. It is obviously not feasible to create a process for each request, even if Multi-processes can improve the utilization of hardware resources, but the startup and destruction of processes consume a large amount of system performance, resulting in a decrease in program execution performance. Therefore, in order to further improve the processing capability of concurrent operations, the concept of multi-threading is divided on the basis of processes.

1.2.1 Take Watching Video as an Example

Opening and watching a video can be understood as realizing a "process". When watching, you will hear the sound, see the picture, and you can send barrage, etc... These are all multi-threaded implementations.

In short: a program has at least one process, and a process has at least one thread.

2. Multi-thread implementation

In java, if you want to implement multi-threading, you must rely on the thread subject class, and java.lang.Thread is the class responsible for multi-thread operation in java. You only need to inherit the Thread class to become the thread subject class. In order to meet some special requirements , you can also complete the definition by implementing the Runnable interface or the Callable interface.

The specific method is as follows:

  1. Inherit the Thread class and rewrite the run method (no return value)

  2. Implement the Runnable interface and rewrite the run method (no return value)

  3. Implement the Callable interface and rewrite the call method (with a return value and exceptions can be thrown)

2.1 Thread class implements multithreading

By inheriting the Thread class and overriding the run() method of the parent class

public void run()

Define thread class:

class MyThread extends Thread{
    
    
	private String name;
	public MyThread(String name) {
    
    
		this.name = name;
	}
	@Override
	public void run() {
    
    
		for(int i = 0 ; i < 50 ; i++) {
    
    
			System.out.println(this.name + "正在工作中……" + i);
		}
	}
}

Multi-thread start:

public class testThread {
    
    
	public static void main(String[] args) {
    
    
		// 实例化线程对象
		MyThread mt1 = new MyThread("线程一");
		MyThread mt2 = new MyThread("线程二");
		MyThread mt3 = new MyThread("线程三");
		// 启动实例线程对象
		mt1.start();
		mt2.start();
		mt3.start();
	}
}

Operation status:
insert image description here

From the above results, it can be found that it is not executed in sequence, but in a random alternate manner, and the results may be different each time.

From the above code, we can know that in order to achieve multi-threading, we must rely on the start() method of the Thread class to execute. After the thread starts, the run() method will be called by default.

2.2 Runnable interface implements multithreading

Using the Thread class can indeed achieve multi-threading, but it is also easy to find its defects: the limitation of object-oriented single inheritance, so the Runnable interface is used to achieve multi-threading.

The definition of the interface is as follows (the following does not need to be implemented in the code):

@FunctionalInterface
public interface Runnable{
    
    
	public void run();
}

Define thread class:

public class MyThread implements Runnable{
    
    
	private String name;
	public MyThread(String name) {
    
    
		this.name = name;
	}
	@Override
	public void run() {
    
    
		for(int i = 0 ; i<50 ;i++) {
    
    
			System.out.println(this.name + " 正在执行中……" + i);
		}
	}
}

Multi-thread start:

public class testThread {
    
    
	public static void main(String[] args) {
    
    
		// 实例化继承Runnable接口的MyThread类
		Runnable mt1 = new MyThread("线程一");
		Runnable mt2 = new MyThread("线程二");
		Runnable mt3 = new MyThread("线程三");
		
		// 多线程启动
		new Thread(mt1).start();
		new Thread(mt2).start(); 		
		new Thread(mt3).start(); 		
	}
}

Operation status:

insert image description here

The above program instantiates three MyThread classes that inherit the Runnable interface, and then instantiates the Thread class through a constructor public Thread (Runnable target) of the Thread class, and then uses the start() method to realize multi-threading.

The difference between Thread scheme implementation and Runnable scheme implementation:

// Thread类定义:
public class Thread extends Object implements Runnable {
    
    }

That is to say, the Thread class is a subclass of the Runable interface. By directly overriding the run method of the Thread class, it is actually still overriding the run method in the Runnable interface. In fact, there is no difference in essence, but using the Runnable solution is more efficient. It embodies object-oriented thinking, which is somewhat similar to the proxy design pattern.

2.3 Callable interface implements multithreading

The multi-threading implemented by using the Runnable interface can avoid the limitation of single inheritance, but there is another problem that the run method has no return value. In order to solve this problem, a Callable interface is provided.java.util.concurrent.Callable

It is defined as follows:

@FunctionalIterface
public interface Callable<T>{
    
    
	public T call() throws Exception;
}

Common methods of the FutureTask class:

import java.util.concurrent.ExecutionException; // 导入ExecutionException异常包
public FutureTask(Callable<T> callable) // 构造函数:接收Callable接口实例
public FutureTask(Runable runnable,T result) // 构造函数:接收Runnable接口实例,同时指定返回结果类型 
public T get() throws InterruptedException,ExecutionException // 取得线程操作返回结果

A constructor of the Thread class:

public Thread(FutureTask<T> futuretask) //构造方法:接收FutureTask实例化对象

Define the thread body class:

import java.util.concurrent.Callable;
public class MyThread implements Callable<Integer>{
    
    
	private String name;
	public MyThread(String name) {
    
    
		this.name = name;
	}
	@Override
	public Integer call(){
    
    
		Integer sum = 0;
		for(int i = 0 ; i < 500;i++) {
    
    
			System.out.println(this.name + i);
			sum += i;
		}
		return sum;
	}
}

Multi-thread start:

import java.util.concurrent.FutureTask;
public class testThread {
    
    
	public static void main(String[] args)  throws Exception{
    
    
		// 实例化继承Callable接口的MyThread类
		MyThread mt1 = new MyThread("线程一");
		MyThread mt2 = new MyThread("线程二");
		MyThread mt3 = new MyThread("线程三");
		
		// FutureTask类接收继承Callable接口的MyThread的实例
		FutureTask<Integer> ft1 = new FutureTask<Integer>(mt1);
		FutureTask<Integer> ft2 = new FutureTask<Integer>(mt2);
		FutureTask<Integer> ft3 = new FutureTask<Integer>(mt3);
		
		// 启动多线程
		new Thread(ft1).start();
		new Thread(ft2).start();
		new Thread(ft3).start();
		System.out.println(ft1.get());
		System.out.println(ft2.get());
		System.out.println(ft3.get());
	}
}

Operation status:

insert image description here

It is easy to understand from the above code that the implementation of the Callable interface is implemented using generic technology. Inheritance needs to rewrite the call method, and then wrap it through the FutureTask wrapper. After passing in, instantiate the Thread class to achieve multi-threading.

Among them, the FutureTask class is a subclass of the Runnable interface, so the start method of the Thread class can be used to start multi-threading. Readers can assume that the call method is a run method with a return value.

2.3 Multi-thread running status

Any thread has 5 basic states:

2.3.1 Create state

After implementing the Runnable interface and inheriting the Thread class to create a thread object, the thread object is in the created state. At this time, it already has memory space and other resources, but it is still inoperable.

2.3.2 Ready state

After creating a thread object, call the thread's start method to start the thread. After startup, it enters the thread queue and queues up, and the service is scheduled by the CPU.

2.3.3 Running status

When the thread in the ready state obtains the resources of the processor, the thread enters the running state, and the run method will be called automatically at this time.

2.3.4 Blocked state

The running thread is in some special cases, such as: when the current thread calls sleep, suspend, wait and other methods, when other threads running in the current thread call the join method, and when waiting for user input. Only when the cause of the blocking disappears can the thread enter the ready state.

2.3.5 Termination state

When the thread run method ends, or the main() method of the main thread ends, the thread can be in the terminated state. Once the thread dies, it cannot be revived.

3. Multi-threaded common operation methods

  1. Thread naming and acquisition
  2. thread sleep method
  3. thread interrupt method
  4. thread enforcement
  5. thread yield
  6. thread priority

3.1 Naming and acquisition of threads

A thread is an indeterminate running state, and the name is the main label of the thread. Therefore, it should be noted that for the name of the thread, the process name must be set before starting. It is not recommended to change the name of the thread that has already started, or set the same name for different threads.

Its main method:

public Thread(Runnable runnable,String name) //构造函数:实例化线程对象,为线程对象设置名称
public final void setName(String name) // 普通函数:设置线程名字
public final String getName() // 普通函数:获取线程名字

Observe the thread naming operation:

class MyThread implements Runnable{
    
    
	@Override
	public void run() {
    
    
		System.out.println( Thread.currentThread().getName());
		System.out.println( Thread.currentThread().getName());
	}
}

public class ThreadDemo {
    
    
	public static void main(String[] args) {
    
    
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		MyThread mt3 = new MyThread();
		
		new Thread(mt1,"线程一").start();
		new Thread(mt2,"线程二").start();
		new Thread(mt3).start(); // 使用默认的线程名称
		mt1.run(); // 这里直接查看main方法的线程名称
	}
}

Operation status:

insert image description here

Note that the main method is also a thread of the process.

3.2 Thread sleep

The sleep method is defined in java.lang.Thread and implemented by calling Thread.sleep(). Its function is to suspend the execution speed of the thread, then the current thread can be made to sleep, that is, the current thread enters the "blocked state" from the "running state". The sleep method will specify the sleep time, the thread sleep time will be greater than or equal to the sleep time, the thread will be woken up, at this time it will change from "blocked state" to "ready state", and then wait for the scheduling execution of the CPU.

Its main method:

public static void sleep(long millis) throws InterruptedException // 普通函数:设置休眠时间的毫秒数
public static void sleep(long millis,int nanos) throws InterruptedException // 普通函数:设置休眠毫秒数和纳秒数

Define the thread body class:

public class MyThread implements Runnable{
    
    
	@Override
	public void run() {
    
    
		for(int i = 0 ; i<90 ; i++) {
    
    
			System.out.println(Thread.currentThread().getName() + " 正在工作中……" + i );
			if(i == 20) {
    
    
				try {
    
    
					System.out.println(Thread.currentThread().getName() + " 等一会儿就要休息五秒钟了……");
					Thread.sleep(5000); // 当前线程休眠五秒钟
					 System.out.println(Thread.currentThread().getName() + " 已经休息五秒钟了……");
				}catch (InterruptedException e) {
    
    
					System.out.println(Thread.currentThread().getName() + " 休眠被打扰了……");
				}
			}
		}
	}
}

Observe the thread sleep operation:

public class ThreadDemo {
    
    
	public static void main(String[] args){
    
    
		 MyThread mt1 = new MyThread();
		 MyThread mt2 = new MyThread();
		 MyThread mt3 = new MyThread();
		 
		 new Thread(mt1,"线程一").start();
		 new Thread(mt2,"线程二").start();
		 new Thread(mt3,"线程三").start();
	}
}

Operation status:

insert image description here

From the above results, it is easy to know that the thread did not work during the sleep period, while other non-sleep threads continued to work.

3.3 Thread Interruption

The interrupt method is defined in java.lang.Thread and implemented by calling Thread.interrupt(). This method will set the interrupt status bit of the thread, that is, be set to true. Whether the interrupted result thread is terminated, blocked or continues to run to the next step depends on the program itself. The thread will check this interrupt flag bit from time to time to determine whether the thread should be interrupted (that is, whether the interrupt flag value is true). It does not interrupt a running thread like the stop method.

Its main method:

// 以下均为Thread类的方法
public boolean isInterrupted() //普通函数:判断线程是否被中断
public void interrupt() //普通函数:中断线程执行

Define the thread body class:

public class MyThread implements Runnable {
    
    
	@Override
	public void run() {
    
    
		int i = 0;
		while(true) {
    
    
			System.out.println(Thread.currentThread().getName() + " 正在努力工作中……" + i++);
			try {
    
    
				System.out.println(Thread.currentThread().getName() + " 准备休息5秒钟了……");
				Thread.sleep(5000);  // 休眠5秒钟
				System.out.println(Thread.currentThread().getName() + " 已经休息5秒钟了……");
			}catch(InterruptedException e) {
    
    
				System.out.println(Thread.currentThread().getName() + " 被打扰了,不想工作了……");
				break;
			}
		}
	}
}

Observe the thread interruption operation:

public class ThreadDemo {
    
    
	public static void main(String[] args) throws Exception{
    
    
		Runnable mt1 = newyThread();
		Runnable mt2 = newyThread();
		Runnable mt3 = newyThread();
		
		Thread thread1 = new Thread(mt1,"线程一"); //线程一就绪
		Thread thread2 = new Thread(mt2,"线程二"); //线程二就绪
		Thread thread3 = new Thread(mt3,"线程三"); //线程三就绪
		thread1.start(); //线程一启动
		thread2.start(); //线程二启动
		thread3.start(); //线程三启动
		
		// 以下通过利用main线程控制 线程一 中断
		Thread.sleep(6000); //使main方法先休眠6秒钟,即让子线程先运行6秒钟
		if(!thread1.isInterrupted()) {
    
    
			System.out.println("吵闹~~~");
			thread1.interrupt(); //中断线程一的执行
		}
	}
}

Operation status:
insert image description here

The above code thread one is interrupted during sleep, and then breaks directly, that is to say, only threads two and three will work in the future. Here whenever the execution is interrupted, an InterruptedException will be generated.

3.4 Thread enforcement

The join method is defined in java.lang.Thread and implemented by calling Thread.join(). After multi-threading is started, resource preemption and thread body execution will be performed alternately. If some threads are extremely important at this time, that is to say, this object needs to be executed first, you can set it as thread mandatory execution, and other threads will continue to execute after it is completed.

Its main method:

// Thread类方法
public final void join() throws InterruptedException //普通函数:强制执行

Define the thread body class:

public class MyThread implements Runnable{
    
    
	private Thread thread = null;
	public MyThread() {
    
    }
	public MyThread(Thread thread) {
    
    
		this.thread = thread;
	}
	@Override
	public void run() {
    
    
		for(int i = 0; i<50 ; i++) {
    
    
			if(i >= 20 && i <= 25) {
    
    
					try {
    
    
						System.out.println(thread.getName()  + "被迫参与 " + Thread.currentThread().getName() + " 的工作了……" +i);
						thread.join();
					}catch(InterruptedException e) {
    
    
						e.printStackTrace();
					}
			}
			// 以下语句不管上面判断语句是否执行都会执行的
			System.out.println(Thread.currentThread().getName() + " 正在工作中……" +i);
		}
	}
}

Observe thread enforcement action:

public class ThreadDemo {
    
    
	public static void main(String[] args) {
    
    
		Thread mainThread = Thread.currentThread();
		MyThread mt1 = new MyThread(mainThread);
		Thread thread1 = new Thread(mt1,"子线程");
		
		thread1.start();
		for(int i = 0 ;i<20;i++) {
    
    
			try {
    
    
				Thread.sleep(1000); // 每次main线程休眠1秒
				System.out.println(Thread.currentThread().getName() +  "正在工作中……" + i);
			}catch(InterruptedException e) {
    
    
				e.printStackTrace();
			}
		}
		System.out.println("我main线程已经完成了所有任务,从此无法再复生了……");
	}
}

Operation status:
insert image description here

From the above results, it is easy to understand that when the sub-thread passes through for the 20th time, it starts to forcibly execute the main thread task until it is all completed, and it can only be executed completely once. After the main thread finishes executing, the sub-threads can continue to execute.

3.5 Thread concessions

The yield method is defined in java.lang.Thread and implemented by calling Thread.yield(). When multiple threads execute alternately with each other, they often need to preempt resources in turn. If some unimportant threads seize resources but are not in a hurry to execute them, they can temporarily give up the current resources and give them to other resources to execute first. . However, because yeild changes the thread from "running state" to "ready state", this does not guarantee that after the current thread calls the yield method, other threads with the same priority will be able to obtain the execution right, and it may be the current The thread enters the "running state" and continues to run, because it still depends on CPU scheduling.

Its main method:

public static void yield() // 静态函数:线程让步

Define the thread body class:

public class MyThread implements Runnable{
    
    
	private Thread thread = null;
	public MyThread () {
    
    }
	public MyThread(Thread thread) {
    
    
		this.thread = thread;
	}
	@Override
	public void run() {
    
    
		for(int i = 0 ; i<50 ; i++) {
    
    
			System.out.println(Thread.currentThread().getName()  + " 正在工作中……" + i);
			if( i  == 30) {
    
    
				System.out.println(Thread.currentThread().getName() + " 打算将工作交给 "+thread.getName() + "了……");
				Thread.yield(); // 当前线程让步出去
				System.out.println(Thread.currentThread().getName() + " 又想自己工作了……");
			}
		}
	}
}

Observe the thread yield operation:

public class ThreadDemo {
    
    
	public static void main(String[] args) {
    
    
		Thread mainThread = Thread.currentThread();
		Runnable mt1 = new MyThread(mainThread);
		Thread thread1 = new Thread(mt1,"子线程");
		
		thread1.start();
		for(int i = 0 ; i < 40 ; i++) {
    
    
	*运行情况:*		System.out.println(Thread.currentThread().getName() + " 正在工作中……" + i);
		}
	}
}

Operation status:

insert image description here

From the above results, it is easy to understand that the resource scheduling will be temporarily given over to other threads. Of course, this is not strict, because CPU scheduling is still required.

3.6 Thread priority

All created threads are sub-threads, and all sub-threads will maintain the same priority permissions when they start, but if some important threads now want to preempt resources and execute them first, they can modify the priority permissions to achieve .

Remember that when a thread's priority is not specified, all threads carry normal priority.

It should be understood that
the priority is specified by an integer ranging from 1 to 10. 10 represents the highest priority, 1 represents the lowest priority, and 5 is the normal priority, which is the default priority.
The thread with the highest relative priority is given priority when executing. But there is no guarantee that the thread will enter the running state when it starts.
The higher the priority, the more likely it will be executed first.

Its main methods and constants:

public static final int MAX_PRIORITY // 静态常量:最高优先级,数值为10
public static final int NORM_PRIORITY //静态常量:普通优先级,数值为5
public static final int MIN_PRIORITY // 静态常量:最低优先级,数值为1
public final void setPriority(int newPriority) // 普通函数:设置优先级
public final int getPriority() //普通函数:获取优先级

Define the thread body class:

public class MyThread implements Runnable{
    
    
	@Override
	public void run() {
    
    
		for(int i  = 0 ; i<50 ; i++) {
    
    
			System.out.println(Thread.currentThread().getName() + " 正在工作中……" + i);
		}
	}
}

Observe thread priority operations:

public class ThreadDemo {
    
    
	public static void main(String[] args) {
    
    
		Runnable mt1 = new MyThread();
		Runnable mt2 = new MyThread();
		
		Thread thread1  = new Thread(mt1,"线程一");
		Thread thread2  = new Thread(mt2,"线程二");
		
		// 设置优先级
		thread2.setPriority(Thread.MAX_PRIORITY);
		thread1.setPriority(Thread.MIN_PRIORITY);
		
		// 启动
		thread1.start();
		thread2.start();
	}
}

Operation status:
insert image description here

It can be seen from the above that the execution probability of thread 2 is higher than that of thread 1. The previous execution is roughly alternate execution because their priorities are equal, and the default is equal to 5.

4. Thread synchronization and deadlock

When this also leads to a problem: at a certain moment, when this resource is changed in a certain thread, the operations being performed by other threads will also be affected by it.

The following procedure is the ticket sales code and result of the conductor:

Define the thread body class:

public class MyThread implements Runnable{
    
    
	private int ticket = 10; 
	@Override
	public void run() {
    
    
		while(true) {
    
    
				if(ticket<0) {
    
    
					System.out.println(Thread.currentThread().getName() + "的票已经全部售完,此时的票数量为:"+ticket);
					break;
				}
				try {
    
    
					Thread.sleep(1000); // 延迟1秒,使得ticket可以被其它线程充分改变(可能此时的ticket小于等于0了)
				}catch(InterruptedException e) {
    
    
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()  + " 正在售票,还剩余票数为:" + ticket--);
			}
	}
}

Observe the ticket sales status:

public class ThreadDemo {
    
    
	public static void main(String[] args) {
    
    
		// 一份资源
		Runnable mt1 = new MyThread();
		
		// 共享同一份资源
		new Thread(mt1,"售票员A").start();
		new Thread(mt1,"售票员B").start();
		new Thread(mt1,"售票员C").start();
	}
}

Operation status:
insert image description here

The above results are a good illustration of the insecurity of multi-threading. The number of tickets should be greater than or equal to 0, but because a certain A thread is delayed and due to shared resources, some other thread may have sold out the tickets at this time (ticket- - == 0), then thread A continues to execute and the number of votes will be less than 0. To solve problems similar to the above, it is necessary to introduce the concept of thread synchronization and deadlock.

4.1 Thread Synchronization

To solve the problem of data sharing, synchronization must be used. The so-called synchronization means that only one thread can execute the specified code in the same period of time among multiple threads. Other threads must wait for this thread to complete before continuing to execute. Provided in Java There is a synchronized keyword to achieve synchronization. The key to synchronization is to add a "lock" to the code.

There are three types of lock operations:

  1. Synchronized code block
  2. synchronization method
  3. Lock implementation

4.1.1 Synchronous code block implementation

How to use:

synchronized(需要同步的对象){
    
    
    需要同步的操作
}

Define the thread body class:

public class MyThread implements Runnable{
    
    
	private int ticket = 10; 
	@Override
	public void run() {
    
    
		while(true) {
    
    
			// 同步代码块
			synchronized(this) {
    
    
				if(ticket<0) {
    
    
					System.out.println(Thread.currentThread().getName() + "的票已经全部售完,此时的票数量为:"+ticket);
					break;
				}
				try {
    
    
					Thread.sleep(10); // 延迟0.01秒,使得ticket可以被其它线程充分改变(可能此时的ticket小于等于0了)
				}catch(InterruptedException e) {
    
    
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()  + " 正在售票,还剩余票数为:" + ticket--);
			}
		}
	}
}

Observe the ticket sales status:

public class ThreadDemo {
    
    
	public static void main(String[] args) {
    
    
		// 一份资源
		Runnable mt1 = new MyThread();
		
		// 共享同一份资源
		new Thread(mt1,"售票员A").start();
		new Thread(mt1,"售票员B").start();
		new Thread(mt1,"售票员C").start();
	}
}

Operation status:

insert image description here

Readers can also easily understand from the above code that although it does play a role in security, the efficiency of execution has dropped, because only one thread can access the synchronization code block at a time.

4.1.2 Synchronization method implementation

How to use:

It is implemented in the form of function packaging, as follows:

修饰符 synchronized 返回类型 函数名()

Define the thread body class:

public class MyThread implements Runnable{
    
    
	private int ticket = 10; 
	@Override
	public void run() {
    
    
		while(this.sale()) {
    
    }
	}
	public synchronized boolean sale() {
    
    
			if(ticket<0) {
    
    
				System.out.println(Thread.currentThread().getName() + "的票已经全部售完,此时的票数量为:"+ticket);
				return false;
			}
			try {
    
    
				Thread.sleep(10); // 延迟0.01秒,使得ticket可以被其它线程充分改变(可能此时的ticket小于等于0了)
			}catch(InterruptedException e) {
    
    
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()  + " 正在售票,还剩余票数为:" + ticket--);
			return true;
		}
}

Observe the ticket sales status:

public class ThreadDemo {
    
    
	public static void main(String[] args) {
    
    
		// 一份资源
		Runnable mt1 = new MyThread();
		
		// 共享同一份资源
		new Thread(mt1,"售票员A").start();
		new Thread(mt1,"售票员B").start();
		new Thread(mt1,"售票员C").start();
	}
}

Operation status:

insert image description here

4.1.3 Lock lock implementation

Define the thread body class:

package cn.wu;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread implements Runnable{
    
    
    private int ticket = 10;
    private final Lock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
        while(this.sale()) {
    
    }
    }
    public boolean sale() {
    
    
        lock.lock();
        try{
    
    
            if(ticket<0) {
    
    
                System.out.println(Thread.currentThread().getName() + "的票已经全部售完,此时的票数量为:"+ticket);
                return false;
            }
            Thread.sleep(200);
            System.out.println(Thread.currentThread().getName()  + " 正在售票,还剩余票数为:" + ticket--);
        }catch (Exception e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            lock.unlock();
        }
        return true;
    }
}

Observe the ticket sales status:

public class ThreadDemo {
    
    
	public static void main(String[] args) {
    
    
		// 一份资源
		Runnable mt1 = new MyThread();
		
		// 共享同一份资源
		new Thread(mt1,"售票员A").start();
		new Thread(mt1,"售票员B").start();
		new Thread(mt1,"售票员C").start();
	}
}

Operation status:

insert image description here

4.2 Thread deadlock

The so-called deadlock refers to a phenomenon in which two or more threads wait for each other due to competition for resources during the execution process. If there is no external force, they will not be able to advance. The operation of deadlock is generally It may only appear when the program is running. Deadlock is a common problem in multi-threaded development. Too much synchronization may cause deadlock.

Observe the deadlock state:

class firstCorssBridge{
    
    
	public synchronized void tell(secondCorssBridge scb) {
    
    
		System.out.println("张三告诉王五:我先过,你后过,否则你别想过这桥!");
		scb.cross();
	}
	// 以下函数不会执行
	public synchronized void cross() {
    
    
		System.out.println("张三快快乐乐地过桥了……");
	}
}
class secondCorssBridge{
    
    
	public synchronized void tell(firstCorssBridge fcb) {
    
    
		System.out.println("王五告诉张三:我先过,你后过,否则你别想过这桥!");
		fcb.cross();
	}
	// 以下函数不会执行
	public synchronized void cross() {
    
    
		System.out.println("王五快快乐乐地过桥了……");
	}
}


public class DeadLock implements Runnable{
    
    
	private firstCorssBridge fcb = new firstCorssBridge();
	private secondCorssBridge scb = new secondCorssBridge();
	
	public DeadLock() {
    
    
		// 启动线程 并执行以下语句
		new Thread(this).start(); // 会运行run函数
		fcb.tell(scb); // 运行到里面时 fcb会等待scb
	}
	@Override
	public void run() {
    
    
		scb.tell(fcb); // 运行到里面时 scb会等待fcb
	}
	
	public static void main(String[] args) {
    
    
		new DeadLock();
	}
}

Operation status:
insert image description here

Note in red above: The two are already in a waiting state for each other, and the subsequent code will not be executed.

If the reader does not understand the above code, you can add it below the new Thread(this).start(); // will run the run function statement:

try {
    
    
		Thread.sleep(100); // 休眠0.1秒钟
	}catch(InterruptedException e) {
    
    
		e.printStackTrace();
	}

The result is:

insert image description here

This is effectively a "lock within a lock" situation.

5. Background daemon thread

There are two types of threads in Java, user threads and daemon threads. Daemon thread (Daemon) is a thread service thread running in the background. When user threads exist, daemon threads can exist at the same time. However, when user threads do not exist, daemon threads will all disappear.

Main operation method:

public final setDaemon(boolean on) throws Exception // 普通函数:是否设置为守护线程
public  final boolean isDaemon() //普通函数:  判断是否为

5.1 Observe daemon thread operation

class MyThread implements Runnable{
    
    
	private int times;
	public MyThread(int times) {
    
    
		this.times = times;
	}
	@Override
	public void run() {
    
    
		for(int i = 0 ; i<times;i++) {
    
    
			if(Thread.currentThread().isDaemon()) {
    
    
				try {
    
    
					Thread.sleep(10); // 如果是守护线程,则休眠0.01秒钟
				}catch(InterruptedException e) {
    
    
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName() + " 正在工作中……"+i);
		}
	}
}


public class testDemo {
    
    
	public static void main(String[] args) {
    
    
		MyThread mt1 = new MyThread(4);
		MyThread mt2 = new MyThread(100); //守护线程的循环次数远多于用户线程
		
		Thread thread1 = new Thread(mt1,"用户线程");
		Thread thread2 = new Thread(mt2,"守护线程");
		thread2.setDaemon(true); //thread2设置为守护线程
		
		thread1.start();
		thread2.start();
	}
}

Operation status:
insert image description here

From the above, we can understand that the daemon thread has ended ahead of time, because the main thread and other user threads have all disappeared.

5.2 Application Scenarios of Daemon Threads

After the main thread is closed, there is no need to manually close the daemon thread, because it will be closed automatically, which avoids trouble. The Java garbage collection thread is a typical daemon thread. It can be understood simply and rudely that all threads that serve threads without involving resources can be set for the daemon thread.

6. Thread pool

6.1 The concept of thread pool

A container that holds multiple threads, and the threads in it can be used repeatedly, eliminating the need to frequently create threads and consume excessive resources without repeatedly creating threads.

6.2 Why introduce thread pool?

If there are too many concurrent threads, and each thread ends after executing a short task, frequent creation of threads will greatly reduce the efficiency of the system, because frequent creation and destruction of threads takes time, and threads also belong to Valuable system resources, therefore, the thread pool is created to enable threads to be reused.

6.3 What are the benefits of thread pool?

  • Reduce resource consumption, reduce the number of threads created and destroyed, each worker thread can be reused, and can perform multiple tasks
  • Improve the response speed, do not need to create threads frequently, if there are threads, they can be used directly, avoiding system deadlock
  • Improve thread manageability

6.4 Core Idea: Thread Reuse

Simple instance of Runnable:

package cn.wu;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        // 1.创建一个线程池,指定线程的数量为4
        ExecutorService pools = Executors.newFixedThreadPool(4);
        // 2.添加线程任务
        Runnable target = new MyRunnable();
        pools.submit(target); // 第一次提交任务,此时创建新线程
        pools.submit(target); // 第二次提交任务,此时创建新线程
        pools.submit(target); // 第三次提交任务,此时创建新线程
        pools.submit(target); // 第四次提交任务,此时创建新线程
        pools.submit(target); // 第五次提交任务,复用之前的线程
        pools.shutdown(); // 当所有任务全部完成后才关闭线程池
//        pools.shutdownNow(); // 立即关闭线程池

    }
}
class MyRunnable implements Runnable {
    
    
    @Override
    public void run() {
    
    
        for(int i = 0 ; i<10 ; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+"正在执行任务…  "+i);
        }
    }
}

result:

Callable simple example:

package cn.wu;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        // 1.创建一个线程池,指定线程的数量为4
        ExecutorService pools = Executors.newFixedThreadPool(4);
        try{
    
    
            long start = System.currentTimeMillis();
            // 2.添加线程任务
            Future<String> t1 = pools.submit(new MyCallable(100000)); // 提交求出1-100000和的线程任务
            Future<String> t2 = pools.submit(new MyCallable(200000)); // 提交求出1-200000和的线程任务
            Future<String> t3 = pools.submit(new MyCallable(300000)); // 提交求出1-300000和的线程任务
            Future<String> t4 = pools.submit(new MyCallable(400000)); // 提交求出1-400000和的线程任务
            Future<String> t5 = pools.submit(new MyCallable(500000)); // 提交求出1-500000和的线程任务
            System.out.println(t1.get());
            System.out.println(t2.get());
            System.out.println(t3.get());
            System.out.println(t4.get());
            System.out.println(t5.get());

            long end = System.currentTimeMillis();
            System.out.println("采用多线程所耗时间为:"+(end-start)*1.0/1000+"s");
            start = System.currentTimeMillis();
            long sum = 0;
            for(int i = 1 ; i<=100000 ; i++) {
    
    
                sum += i;
            }
            System.out.println("最终结果为:"+sum);
            sum = 0;
            for(int i = 1 ; i<=200000 ; i++) {
    
    
                sum += i;
            }
            System.out.println("最终结果为:"+sum);
            sum = 0;
            for(int i = 1 ; i<=300000 ; i++) {
    
    
                sum += i;
            }
            System.out.println("最终结果为:"+sum);
            sum = 0;
            for(int i = 1 ; i<=400000 ; i++) {
    
    
                sum += i;
            }
            System.out.println("最终结果为:"+sum);
            sum = 0;
            for(int i = 1 ; i<=500000 ; i++) {
    
    
                sum += i;
            }
            System.out.println("最终结果为:"+sum);
            end = System.currentTimeMillis();
            System.out.println("采用单线程所耗时间为:"+(end-start)*1.0/1000+"s");
        }catch(Exception e) {
    
    
            e.printStackTrace();
        }
    }
}
class MyCallable implements Callable<String> {
    
    
    private int num;
    public MyCallable(int num) {
    
    
        this.num = num;
    }
    @Override
    public String call() throws Exception {
    
    
        long sum = 0;
        for(int i = 1 ; i <= num ; i++) {
    
    
            sum += i;
        }
        return Thread.currentThread().getName()+
                "任务执行的最终结果为:"+sum;
    }
}

result:

insert image description here

Guess you like

Origin blog.csdn.net/u011397981/article/details/131943723