Introduction to Java multithreading and fairness and code examples
If a thread cannot get CPU running time because all CPU time is robbed by other threads, this state is called "hunger". The thread was "starved to death" precisely because it did not get the chance of CPU running time. The solution to hunger is called "fairness" C, that is, all threads can get a fair chance to run.
The following are the topics discussed in this article:
Causes of hunger in Java
In Java, the following three common causes cause thread starvation:
High priority threads consume CPU time of all low priority threads. The thread is permanently blocked in a state waiting to enter the synchronization block because other threads can always continuously access the synchronization block before it. The thread is waiting for an object that itself (on which wait() is called) is also permanently waiting for completion, because other threads are always continuously awakened.
High priority threads consume CPU time of all low priority threads
You can set a separate thread priority for each thread. The higher the priority, the more CPU time the thread will get. The thread priority value is set between 1 and 10, and the accurate interpretation of the behavior represented by these priority values depends on your application running platform. For most applications, you'd better not change its priority value.
The thread is permanently blocked in a state waiting to enter the synchronization block
Java's synchronous code area is also a hunger factor. Java's synchronous code area does not guarantee which thread is allowed to enter in order. This means that theoretically, there is a risk that a thread trying to enter the synchronization zone will be permanently blocked, because other threads can always get access before it, which is the "hunger" problem, and a thread is "starved to death" because it can't get the chance of CPU running time.
The thread is waiting for an object that itself (on which wait() is called) is also permanently waiting for completion
If multiple threads are executing the wait () method, and calling notify () on them does not guarantee which thread will wake up, any thread may be in the state of continuing to wait. Therefore, there is a risk that one waiting thread will never be awakened, because other waiting threads can always be awakened.
Here is an example of thread starvation deadlock. The code is as follows:
Implementing fairness in Java
Although it is impossible for Java to achieve 100% fairness, we can still improve fairness between processes through synchronous structure.
First, let's learn a simple synchronization code:
If more than one thread calls the dosynchronized () method, other threads will be blocked until the first thread to obtain access is completed. In this scenario where multiple threads are blocked, the next thread to obtain access is not guaranteed.
Use lock mode instead of synchronization block
In order to improve the fairness of waiting threads, we use locking instead of synchronous blocks.
Notice that dosynchronized () is no longer declared synchronized, but lock Lock() and lock Unlock() instead.
The following is an implementation of lock class:
Note the above implementation of lock. If multiple threads access lock () concurrently, these threads will block access to the lock () method. In addition, if the lock has been locked (proofreading note: This refers to when islocked is equal to true), these threads will block in the wait() call of the while (islocked) loop. Remember that when a thread is waiting to enter lock (), it can call wait () to release the synchronization lock corresponding to its lock instance, so that multiple other threads can enter lock () method and call wait () method.
This time, looking at dosynchronized (), you will notice the comment between lock () and unlock (): the code between these two calls will run for a long time. Further imagine that this code will run for a long time, compared with entering lock () and calling wait (). This means that most of the time spent waiting to enter the lock and enter the critical area is spent waiting for wait (), rather than being blocked in trying to enter the lock () method.
As mentioned earlier, the synchronization block does not guarantee the access of multiple threads waiting to enter. Similarly, when notify() is called, wait() does not guarantee that the threads will wake up (for why, see thread communication). Therefore, there is no difference between this version of lock class and dosynchronized() version in terms of ensuring fairness.
But we can change that. The current lock class version calls its own wait() method. If each thread calls wait() on a different object, only one thread will call wait() on the object. The lock class can decide which object can call notify() on it, so it can effectively choose which thread to wake up.
Fair lock
Let's talk about transforming the above lock class into a fair lock fairlock. You will notice that the new implementation is slightly different from the synchronization and wait () / notify () in the previous lock class. To be exact, how to achieve fair lock design from the previous lock class is a gradual design process. Each step is to solve the problems in the previous step: nested monitor lockout, sloped conditions and missed signals. Although these discussions are beyond the scope of this article, the contents of each step will be discussed on a special topic. Importantly, every thread calling lock () will enter a queue. After unlocking, only the first thread in the queue is allowed to lock the farlock instance, and all other threads will be in a waiting state until they are at the head of the queue.
First, notice that the lock () method is no longer declared as synchronized. Instead, the code that must be synchronized is nested in synchronized.
Fairlock creates a new instance of queueobject and queues each thread calling lock (). The thread calling unlock () will get the queueobject from the queue header and call donotify () on it to wake up the thread waiting on the object. In this way, only one waiting thread is awakened at the same time, not all waiting threads. This is also the core of fairlock fairness.
Note that in the same synchronization block, the lock status is still checked and set to avoid slip conditions.
Also note that the queueobject is actually a semaphore. The dowait () and donotify () methods store signals in the queueobject. Do this to avoid a thread calling queueobject Dowait () was previously called unlock () by another and then called queueobject The thread of donotify() re entered, resulting in signal loss. queueObject. The dowait () call is placed outside the synchronized (this) block to avoid being locked by the nested monitor, so another thread can be unlocked as long as no thread is executed in the synchronized (this) block of the lock method.
Finally, notice the queueobject How dowait() is called in the try C catch block. When the interruptedexception is thrown, the thread can leave lock () and needs to be removed from the queue.
Performance considerations
If you compare the lock and fairlock classes, you will notice that lock () and unlock () in the fairlock class have more areas to explore. This extra code will cause fairlock's synchronization mechanism to be implemented slightly slower than lock. How much impact there is also depends on the execution time of the application in the fairlock critical area. The longer the execution time, the less the burden and impact of fairlock. Of course, this is also related to the frequency of code execution.
summary
The above is all about the introduction and code examples of Java multithreading hunger and fairness. I hope it will be helpful to you. Interested friends can continue to refer to this site: multi thread deadlock of Java programming, simple implementation code of inter thread communication, analysis of synchronized locked objects of Java programming, code examples of distributed lock implemented by redisson of Java programming, etc. if you have any questions, you can leave a message at any time, and the editor will reply to you in time. Thank you for your support!