Four classic cases of Java-thread safety and thread pool

singleton pattern

For some objects, there should be only one instance in a program (Guaranteed by people alone is unreliable and guaranteed by grammar) You can use the singleton mode

In the singleton mode, the instantiation of objects is restricted and can only create one more

There are two types of singleton mode: hungry man mode and lazy man mode

Hungry man mode:

I'm hungry, I can't eat (create), just create an instance when the class is defined

Class Singleton{
private static singleton instance= new singleton();
public static Singleton getInstance(){
return instance;
}
Private Singleton(){
}(防止new实例)
};

static static The actual effect has nothing to do with the literal meaning. The actual meaning is that class attributes/class methods (corresponding to instance attribute instance methods) class objects can only create one

The effect is that no matter how many instances you get, it is the same instance and cannot be new

Lazy mode:

ε=(´ο`*))) Create it when you use it

class Singletonlazy {
         private static Singgletonlazy instance = null;
         public static Singletonlazy getInstance(){
         If(instance == null){
            Instance = new Singletonlazy();
}
Return instance;
}
}

 So back to multi-threading, are the hungry and lazy modes thread-safe?

Hungry man mode get is just multi-threaded reading, no problem

Lazy man mode calls get for the first time, some places are read and some places are written after the instance is created, it is safe (one thread is reading, one thread is creating, and it is read before it is created, and it is created again)

So we can choose to lock it

synchronized(SingletonLazy.class){
 If(instance == null){
            Instance = new Singletonlazy();
}}

But if it is written like this, each judgment operation is also locked, and it is still not possible to run simultaneously in multiple threads (one judgment will block the other) so we can

if(instance==null){
synchronized(SingletonLazy.class){
 If(instance == null){
            Instance = new Singletonlazy();
}}}

In a single thread, it doesn't make sense for us to use two identical ifs in a row, but one synchronized is another. 

The hungry man mode and the lazy man mode are a kind of design idea, not a fixed code, let's give another example

1. The hungry man reads all 10g into the memory and then allows the user to view and modify

2. The lazy man only reads one point at a time as the user turns the page and continues to read the follow-up content

producer consumer model

Let's first understand the blocking queue

What is the difference between a blocking queue and a normal queue? It will block when the queue is empty and try to exit the queue. Trying to enqueue when the queue is full will also block

The producer-consumer model is implemented through blocking queues

Let’s use making dumplings as an example, and take making dumplings as the ultimate goal (there is no link of cooking and eating dumplings)

One person rolls the dumpling wrappers and others wrap them (because the number of rolling pins is limited, the efficiency will be much higher than that of each person rolling the dumpling wrappers by himself)

Producer-consumer model (there must be a curtain to be considered a producer-consumer model)

Producer: Responsible for rolling dumpling wrappers

Curtain: blocking queue

Consumer: the person responsible for making dumplings

What are the benefits of the producer consumer model

1. Better decoupling can be achieved (high cohesion means that when writing a function, try to let the code of this function be placed in a centralized manner instead of one piece at a time)

If the direct interaction between the producer and the consumer is highly coupled, if b sends it to a, it may also be broken

If the producer->blocking queue->consumer only thinks about interacting with the blocking queue, then neither of them will affect the other party.

2. Cutting peaks and filling valleys (just like the Three Gorges Dam, water in the rainy season is given to the dry season) to improve the risk resistance of the entire system

Large-scale users access aa at the same time and then synchronize b to b. If there is no strong ability to withstand pressure, it may be sent

producer consumer model

Put a blocking queue between a and b Even if the pressure on a is high, b still fetches data according to the established frequency

Code

 public static void main(String[] args) {
            BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
            Thread productor = new Thread(()->{
            	int n = 0;
            	while(true) {
            		try {
						System.out.println("生产元素"+n);
						queue.put(n);
						n++;
						Thread.sleep(500);//这行代码,没有实际意义 是为了让运行结果更方便观察而已
					} catch (Exception e) {
						e.printStackTrace();
					}
            	}
            });
            productor.start();
            Thread customer = new Thread(()->{
            	while(true) {
            		try {
						System.out.println("消费元素"+queue.take());				
						Thread.sleep(500);
					} catch (Exception e) {
						e.printStackTrace();
					}
            	}
            });
            customer.start();
	    }

Simulate the implementation of a blocking queue

class myqueue{
	int[] queue = new int[100];
	int size = 0;
	int head = 0;
	int tail = 0;
	
	public void put(int a) throws InterruptedException{
		synchronized (this) {
			if(size==queue.length) {
				this.wait();
			}
			
			queue[tail] = a;
			tail++;
			if(tail==queue.length) {
				tail = 0;
			}
			size++;
			this.notify();
		}
	}
	public Integer take() throws InterruptedException{
		int ret = 0;
		synchronized (this) {
			if(size==0) {
				this.wait();
			}
			ret = queue[head];
			head++;
			if(head==queue.length) {
				head=0;
			}
			size--;
			this.notify();
			return ret;
		}
	}
}

Don't worry that this notify wakes up the wait of the current function, because if there is a wait, this step cannot be done

timer 

The method of use is like this

First instantiate a Timer object

Then call its schedule method

The schedule has two parameters. The first parameter is the Timertask object. The run in the rewrite is the business code.

The second parameter is time, an integer unit ms

public static void main(String[] args) {
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {			
				@Override
				public void run() {
					System.out.println("任务");
								}
			}, 1000);
	    }

You may ask how good it is for me to use sleep directly

sleep blocks the thread and can't do anything, but you can do other things while the timer is waiting

command indicates the task after indicates how long to pause

This schedule (schedule) is a table, that is to say, multiple elements can be placed

Simulation implementation

There are many tasks to be organized inside Timer

There also needs to be a thread to scan the inside of the timer through this thread to see which time it is time to execute

Use a priority queue to organize these tasks Use a priority queue to sequence time

System.currentTimeMillis() system current timestamp\

PriorityQueue<MyTask> queue

This queue will be accessed by multiple threads at the same time

The schedule may be called in multiple threads, adding elements to the queue for each call

There is also a need for a dedicated thread to perform tasks internally.

It involves entry and exit, so it is not safe

PriorityBlockingQueue uses a blocking queue

public void schedule()

When entering the structure, create a thread loop to continuously try to get the first element of the queue and determine whether the element is ready (a large number of loops are generated, idling, and there are no practical tasks)

So let him wait for a while after the judgment is over (not ready) (sleep does not work even if it is ready, sleep is still resting, you can use wait wait to wake up in advance) (wake up and judge every time you insert a task)

All unified timestamps If the specified time is greater than the current time (it’s not time yet), it will be inserted back 

Execute if it arrives

The priority queue needs to define the priority principle, implement the Comparable interface and rewrite compareTo, so it must be written in the class of the compared element

Whoever subtracts this return can write code to try (if the first parameter is less than the second parameter, it will return a negative number, if it is equal to it, it will return 0, if it is greater than it, it will return a positive number. And it is required to return an int and force it)

private Object locker = new Object();

wait and notify are both files in java.util just create an object class

In principle, you only need to judge the first element of the queue (the waiting time is the least) but it is necessary to prevent the newly added element from being smaller than the first element of the queue (so add notify so it is not possible to use sleep) and wake up the thread every time a new element is added. If this If the time of the new element is earlier than the previous time, then it will be judged according to the new team leader element. If it is later than the previous first element time, it doesn’t matter, then wait.

 a misunderstanding

Here this synchronized uses the locker as the object, but if queue.put does not use the locker resource, can it not be locked? It is not that since it locks the locker, other threads cannot get the locker when running the code block (already blocked lock) will still block so the goal is achieved

Thread Pool

Processes are too heavy, expensive to create and destroy

Thread is an optimization for the above problems (share a set of system resources)

But if it is more frequent, the thread can't hold it anymore

advanced optimization

1. Thread pool

2. Coroutine (fiber) lightweight thread 

The eight threads are created and placed in the pool. You need to use them directly from the thread pool and return them after use. There is no need to create and destroy them.

 Equivalent to ten threads to run this one hundred tasks

Simulation implementation

This thread is running continuously as long as something is entered in the submit, it will run

 

Guess you like

Origin blog.csdn.net/chara9885/article/details/130739038
Recommended