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
-
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)
-
-
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 blockA 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!