Multithreaded high concurrency serial one (synchronized lock)

Synchronized is the most commonly used way to ensure thread safety in Java Concurrent Programming. Its use is relatively simple and its principle is not difficult. Refer to blog: synchronized principle Here I just summarize:

  1. When synchronized decoration is in the code block, you can see from the compiled code that it is completed by the instructions monitorenter and monitorexit. Enter monitorenter+1,
  2. When synchronized decoration is applied to a method, it is implemented through compiled governance, not through the instructions monitorenter and monitorexit, but it has more acc ﹣ synchronized identifier in the constant pool, and monitors are operated according to this identifier. Therefore, this method is also called implicit implementation, and its essence is the same

synchronized use

  1. As mentioned above, synchronized can be written on methods or code blocks, but it is possible that the lock type is different. If it is written on methods, the lock is the object of the current class. When it is used on code blocks, the lock object can be specified. If it acts on a static code block, the class object of the specified class is locked
  2. Synchronized lock belongs to the fully automatic type, which locks and unlocks automatically. In addition, in the process of code execution, if thrown abnormally, synchronized will also release the lock automatically
  3. synchronized locks are reentrant locks, that is, the way the current thread calls A. In the A method, the B method can be invoked, and it can be called directly without competing locks. The jvm is designed to prevent deadlock. Therefore, you should also understand the reason why the current thread cannot access method a when other threads access other methods. In other words, when T1 thread accesses method a, T2 thread also accesses method a, which is definitely not allowed. Needless to say, because the lock is an object, and the object is locked by T1 thread, T2 thread certainly cannot obtain the lock. That's the same. When T2 thread accesses method B, is not the same object locked in method B, that is to say, T2 thread also needs to acquire the lock of the object locked in method a by T1 thread.
  4. threadlocal thread private variable. For example, you can see:
public class ThreadLocalTest {
	class Person {
		String name = "zhangsan";
	}
	@Test
	public void threadLocal1() {
		ThreadLocal<Person> tl = new ThreadLocal<Person>();
		Person person = new Person();
		tl.set(person);
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("New start thread"+tl.get());
			}
		}).start();
		System.out.println(tl.get());
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

The newly started thread print person is null, that is to say, the object set through threadlocal can only be used in the current thread, and other threads get null.

  1. volatile keyword, Please click jump
  2. I'll focus on notify and wait, because the pit in it fell in without any attention, I wrote one before Let me give another example of producers and consumers: Requirement: there are 2 producer threads and 10 consumer threads. If the container is full, the producer will pause. If the container is empty, the consumer will pause
public class ProducerAndConsumer {
	//There are 2 producer threads and 10 consumer threads. If the container is full, the producer pauses. If the container is empty, the consumer pauses
	final private LinkedList list = new LinkedList();
	final private int maxValue = 10;
	/**
	 * Producer, as required: stop when container is full.
	 */
	public synchronized void producer(Object o) {
		/**
		 * The initial implementation uses if, but why change it to if and then to while: (this is a pit, which is easy to drop)
		 * 	1.You think, if the container is full at this time, and the first thread arrives here, judge the if logic, execute the wait method to wait, and [release the current object lock]
		 *  2.Then the cpu calls which thread is randomly selected. If another producer thread is called at this time, the wait method is also executed.
		 *  3.At present, there are two threads in wait, and then the cpu execution consumer thread consumes one thread. Now there are nine in the container,
		  *        Then the consumer thread wakes up the two producer threads through notifyall. Then:
		 *  	3.1 First, the first producer thread starts to execute, starting from the wait, adding elements, the container is full again, releasing the lock
		 *  	3.2 Then the second producer thread starts to execute, also from the wait, because the while loop is not used, so
		 *  		It will execute directly, that is, add elements, and the container size will not be 10.
		 *  4.Therefore, 99% of the scenarios when using wait are used with the while loop
		 *  5.If wait and notify are not written in the synchronized lock, the IllegalMonitorStateException exception will be thrown, but
		 *  	Why do you have to be in a synchronized lock? There must be a wait call after a notify call. [reference blog] (https://www.cnblogs.com/set-cookie/p/8686218.html)
		 */
//		if(list.size()==maxValue) {
		while(list.size()==maxValue) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		list.add(o);
		//There is something in the container. The thread thread waiting for wakeup consumes (the thread wakeup includes producer thread and consumer thread). Its main purpose is consumer thread
		this.notifyAll();
	}
	/**
	 * Consumer
	 */
	public  synchronized Object consumer() {
		while(list.size()==0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//I take something away, wake up the waiting thread for production (including producer thread and consumer thread), the main purpose of which is producer thread
		Object o = list.removeFirst();
		this.notifyAll();
		return o;
	}
	public static void main(String[] args) {
		ProducerAndConsumer pcTest = new ProducerAndConsumer();
		//Start consumer thread
		for(int i=0;i<10;i++) {
			new Thread(()->{
				for(int j=0;j<10;j++) {
					System.out.println(pcTest.consumer()+"aa");
				}
			},"c"+i) .start();
		}
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//Start producer thread
		for(int i=0;i<2;i++) {
			new Thread(()->{
				for(int j=0;j<30;j++) {
					pcTest.producer(Thread.currentThread().getName());
				}
			},"p"+i) .start();
		}
	}
  1. Thread stop, interrupt method, thread.stop(), is to kill you directly. There is no room for you to kill directly, and you will not enter the catch. So don't use the stop method unless you have to. When thread.interrupt(), the thread can still enter the code in the following catch. But these two methods are not recommended. The better way is to define variables
public class ThreadStopByArgs {
	/**
	 * In the form of variable, the thread stops
	 *
	 */
	public static void main(String[] args) {
		ThreadRunnerTest threadRunnerTest = new ThreadRunnerTest();
		Thread thread = new Thread(threadRunnerTest);
		thread.start();
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("stop");
		threadRunnerTest.changeFlag();
	}
}
class ThreadRunnerTest implements Runnable {
	private volatile boolean flag = true;
	public void run() {
		int i=0;
		while(flag) {
			//System.out.println(i + +); when testing multithreading, if you use syso output, be careful, because there is sync in syso
		}
	}
	public void changeFlag(){
		flag = false;
	}
}
  1. Other methods, such as sleep, yield, join, stop, interrupt and so on, please learn by yourself. There is nothing to remember Locks must be locked on the basis of the same object, like: can you have more than one door in your bathroom? In other words, when accessing shared resources, only one door can let you access them, so as to control the security of shared resources and threads. The meaning of safety is not only for the type of traffic accident, as long as the expected result is changed because it is interfered by other factors in the implementation process, it is unsafe.

Tags: Programming Java jvm

Posted on Sun, 03 Nov 2019 09:23:13 -0800 by palpie