Concurrency error and deadlock in java

2.8.6 concurrent errors

2.8.6.1 concept

Concurrency error: when multiple threads operate on the same object, the continuous multi line operations in the thread body may not be able to be executed continuously. It is likely that the operation is only partially completed and the time slice is suddenly exhausted. At this time, another thread grabs the time slice and directly takes away and accesses the incomplete data (the incomplete data is logically the wrong data)

Root cause: multiple threads share the same data

Direct cause: the continuous multi line statements in the thread body may not be able to be executed continuously. It is likely that the operation is only half completed and the time slice is suddenly exhausted
At this time, another thread just grabs the time slice and directly takes away the incomplete data error data

Fuse: the time slice suddenly runs out

Interview questions: what is the relationship between concurrent errors and concurrent modification exceptions?
Concurrent modification exception: it is a verification mechanism to avoid concurrent errors. When the iterator finds its own ideal modCount and the modCount fed back by the collection
In case of inconsistency, it will be considered that another thread is operating the collection at the same time, so it will actively throw the exception ConcurrentModificationException

2.8.6.2 resolving concurrent errors

Locking:

The first syntax level lock = mutex

  1. Using the synchronized modifier

    Mutex = mutex tag = lock tag = lock flag = Monitor = Monitor

    Usage:

    • Modifying code blocks

      synchronized(Critical resources){
      
      	Operations that need to be performed continuously 1;
      
      	Operations that need to be performed continuously 2;
      
      			···············;
      
      }
      
    • Modify the whole method

      public synchronized void add(){
        
      }
      //Equivalent to
      public void add(){
      	synchronized(){
      	}
      }//It's all about locking objects
      

      Note: even if synchronized is added to a method, it locks the object, and the object that calls the method is locked
      In the Java world, only every object has a lock tag, so locking can only lock objects.

    *: Vector Hashtable StringBuffer is thread safe because a large number of underlying methods are decorated with synchronized

    *: the lazy type of singleton mode needs synchronized to modify the getter method

    public static  synchronized  Sun  getInstance(){
    									return x;
    }
    

    *: what are the features of synchronized? It cannot be inherited by subclass methods
    Thread safe methods in the parent class, when inherited by the child class, have no synchronized modification and must be overridden (overridden)

    *: if synchronized modifies a static method, it is equivalent to locking the. Class of this class (actually locking the meta object of this class)

  2. Locking of the second object-oriented idea = reentrant lock

    Java. Util. Concurrent. Locks. Reentrantlock (starting with JDK 5.0): the reentrant lock of the toolkit of the java package

    Reentrantlock: lock (lock) unlock (unlock): put in finally {}

    In addition, the construction method of reentrant lock can be specified by parameters
    Fair lock or non fair lock default non fair lock

    *: before JDK6.0, the Lock mechanism was much more efficient than synchronized
    JDK 6.0 started to modify the underlying implementation of synchronized again, adding a bunch of new concepts (spin mechanism of biased lock and lightweight lock)
    Starting from JDK6.0, the performance of synchronized and Lock is comparable

    *: ReentrantLock can pass fair locks and non fair locks in the construction method (fair or not for the first incoming thread)

    Fair lock: new reset lock (true);

    Solving concurrent error cases

    import java.util.concurrent.*;
    public class TestCurrentError{
    public static void main(String[] args){
    Student stu=new Student("zml","ma'am");
    Lock lock=new ReentrantLock();
    Print p=new Print(stu);
    Change c=new Change(stu,lock);
    p.start();
    c.start();
    }
    }
    class Change extends Thread{
    	Student stu;
    	Lock lock;
    	public Change(Student stu,Lock lock){
    		this.stu=stu;
    		this.lock=lock;
    		}
    	@Override
    	public void run(){
    		boolean isOkay=true;
    		while(true){
    			//synchronized(stu){
    				try{
    				lock.lock();//ReentrantLock lock
    			if(isOkay){
    				stu.name="Tony Leung";
    				stu.gender="man";
    				}else{
    					stu.name="Maggie Cheung";
    					stu.gender="ma'am";
    				}
    				isOkay=!isOkay;
    			}finally{
    				lock.unlock();//Unlocking
    				}
    			}
    			//}
    		}
    	}
    
    class Print extends Thread{
    	Student stu;
    	public Print(Student stu){
    		this.stu=stu;
    		}
    	@Override
    	public void run(){
    		while(true){
    			synchronized(stu){//synchronized for locking
    				System.out.println(stu.name+":"+stu.gender);}
    			}
    		}
    	}
    class Student{
    String name;
    String gender;
    public Student(String name,String gender){
    	this.name=name;
    	this.gender=gender;
    }
    }
    

    2.8.7 deadlock

    If mutex tags are used too much or improperly, multiple threads will hold each other's desired resources without releasing them
    Apply for the resources already held by the other party, so that both enter the block

    A classic case of Deadlock: starvation in the United Nations between China and the United States

    public class TestDeadLock{
    public static void main(String[] args){
    	Resturant r=new Resturant();
    	Resturant.Chinese c=r.new Chinese();
    	Resturant.American a=r.new American();
    	c.start();
    	a.start();
    	
    
    }}
    
    class Resturant{
    	Object knife=new Object();
    	Object chopsticks=new Object();
    
    	class Chinese extends Thread{
    		@Override
    		public void run(){
    			System.out.println("The Chinese entered the restaurant");
    			synchronized(knife){
    				System.out.println("The Chinese took the knife");
    				try{Thread.sleep(100);}catch(Exception e){e.printStackTrace();}
    				synchronized(chopsticks){
    					System.out.println("The Chinese got chopsticks");
    					}
    				}
    				System.out.println("Chinese people can eat noodles normally");
    			}
    		}
    
    		class  American extends Thread{
    			@Override
    			public void run(){
    				System.out.println("The Americans entered the restaurant");
    				synchronized(chopsticks){
    					System.out.println("The Americans took chopsticks");
    					try{Thread.sleep(100);}catch(Exception e){e.printStackTrace();}
    					synchronized(knife){
    						System.out.println("The Americans got the knives");
    						}
    					}
    					System.out.println("Americans can eat steak normally");
    				}
    			}
    	}
    
    

    2.8.8 how to solve the deadlock problem

    One space: waiting pool for objects

    Three methods: Object class

    wait(): let the current thread release the lock mark of the object and enter the waiting pool of the object calling the method

    notify(): call method from

    ​ notifyAll():

    *: these three methods are all methods of Object class, not thread. Each Object has a waiting pool, and each Object may operate thread

    *: these three methods can only be used under the premise of holding the lock tag, so they must appear in the {} of synchronized. If you directly operate the waiting pool without getting the lock tag of the object, it will not only fail, but also cause the runtime exception illegalMonitorStateException

    Concepts of lock pool and wait pool

    In Java, each object has two pools, a monitor pool and a wait pool

    Lock pool: it is assumed that thread A already owns the lock of an object (Note: it is not A class), while other threads want to call A synchronized method (or synchronized block) of the object, because these threads must obtain the ownership of the lock of the object before entering the synchronized method of the object, but the lock of the object is currently owned by thread A, So these threads enter the lock pool of the object.

    Waiting pool: if A thread A calls the wait() method of an object, thread A will release the lock of the object (because the wait() method must appear in synchronized, so thread A will have the lock of the object before the wait() method is executed), and thread A will enter the waiting pool of the object. If another thread calls the notifyAll() method of the same object, all threads in the waiting pool of the object will enter the lock pool of the object and prepare to compete for the ownership of the lock. If another thread calls the notify() method of the same object, only one thread (random) in the object's waiting pool will enter the object's lock pool

    What is the difference between waiting pool and lock pool?

    Each object in java has a share of space, and is used to store Thread objects

    Lock pool: stores threads that want to get the lock mark of an object, but have not yet got the lock mark

    Waiting pool: the thread that has already got the lock tag but doesn't want to form mutual restriction with others, so it actively releases the lock tag

    There are three differences

    Whether resources need to be released when entering: Lock pool does not need to be released; Waiting pool needs

    Whether to call method when leaving: no operation is needed when leaving lock pool; notify()/noyifyAll() is required to leave the waiting pool

    What state to go after leaving: leave the lock pool and directly return to ready; Leave the waiting pool and go straight to the lock pool

    *: using wait() and notify() to realize the alternate execution of two threads

    public class TestSwitchThread{
    	public static void main(String[] args){
    		Right r=new Right();
    		Left l=new Left(r);
    		l.start();
    
    	}
    }
    class X {
    	static Object obj=new Object();//Define a lock mechanism (lock object)
    	}
    
    class Left extends Thread{
    	Right r=new Right();
    	public Left(Right r){//Data sharing by parameter transmission
    		this.r=r;
    		}
    	@Override
    	public void run(){
    
    			synchronized(X.obj){
    				r.start();//Start the right foot thread when the left foot gets the ownership of the lock [at this time, the right foot will always perform the operation after the left foot, and there will be no stuck phenomenon]
    				for(int i=0;i<1000;i++){
    				System.out.println("Left foot");//1
    				try{X.obj.wait();}catch(Exception e){e.printStackTrace();}//2 left foot wait, right foot execute
    				X.obj.notify();//6 when the left foot is finished, inform the right foot to enter the lock pool
    				}
    			}
    		}
    	}
    class Right extends Thread{
    	@Override
    	public void run(){
    
    			synchronized(X.obj){
    				for(int i=0;i<1000;i++){
    				System.out.println("							Right foot");//3
    				X.obj.notify();//4 after the execution of the right foot, remind the left foot waiting for the pool to enter the lock pool
    				try{X.obj.wait();}catch(Exception e){e.printStackTrace();}//5 right foot wait, left foot execute
    				}
    			}
    		}
    	}
    

Thank you for browsing and liking, let's learn java happily!

Tags: Java Programming JDK Multithreading Concurrent Programming

Posted by Loathor__Healer on Fri, 23 Jul 2021 05:20:23 +0930