"Summary" Java thread synchronization mechanism deeply elaborated

Source: Internet
Author: User
Tags class definition object object semaphore

Original: http://hxraid.iteye.com/blog/667437

We can run various computer software programs on the computer. Each running program may include multiple independent running threads (thread).
Thread is a self-running program that has its own dedicated run stack. It is possible for threads to share some resources with other threads, such as memory, files, databases, and so on.
Conflicts can arise when multiple threads read and write to the same shared resource at the same time. At this time, we need to introduce the thread "synchronization" mechanism, that is, there is a first served between the threads, you can not swarm squeeze up to grab a regiment.
The word synchronization is translated from the English synchronize (making simultaneous occurrences). I don't understand why this is a very misleading word. Since we all use it, we have to do it.
The true meaning and literal meaning of thread synchronization is exactly the opposite. The true meaning of thread synchronization is actually "queued": there are several threads to queue, one to operate on shared resources, and not to operate concurrently.

So the 1th thing to keep in mind about thread synchronization is that thread synchronization is thread queuing. Synchronization is queued. The purpose of thread synchronization is to avoid thread "synchronous" execution. This is really a boring tongue twister.
With regard to thread synchronization, the 2nd word "sharing" needs to be kept in mind. Only read and write access to shared resources requires synchronization. If it is not shared resources, then there is no need to synchronize at all.
The 3rd thing to keep in mind about thread synchronization is that only "variables" need to be accessed synchronously. If the shared resource is fixed, then it is equivalent to "constant", and the thread reads the constant and does not need synchronization. At least one thread modifies the shared resource, in which case the threads need to be synchronized.
With regard to thread synchronization, the 4th to remember is that multiple threads may have the same code or different code to access the shared resource, or if the same code is executed, as long as the code of those threads accesses the same mutable shared resource, the threads need to be synchronized.

To deepen your understanding, here are a few examples.
There are two buyers, their work content is the same, are followed by the following steps:
(1) Go to the market and find and purchase potential samples.
(2) Return to the company and write a report.
These two people work in the same way, they all need to buy samples, they may buy the same kind of samples, but they will never buy the same sample, they do not have any shared resources. So, they can do their own work, do not interfere with each other.
These two buyers are equivalent to two threads, and two buyers follow the same work steps, equivalent to two threads executing the same piece of code.

Add a work step to the two buyers below. The purchaser is required to arrange his/her work plan according to the information posted on the company's "bulletin Board".
The two buyers are likely to go to the front of the bulletin board at the same time and watch the information on the bulletin boards. None of this is a problem. Because the bulletin board is read-only, neither of these buyers will be able to modify the information written on the notice boards.

Add a role below. An office executive at this time, also went to the bulletin board, ready to modify the information on the bulletin boards.
If the administrative staff arrives at the bulletin board first, and is revising the contents of the bulletin board. Two buyers this time, it happened. These two buyers must wait for the administrative staff to complete the changes before they can view the revised information.
If the executives arrive, two buyers are already watching the bulletin board. Then the executives need to wait for two buyers to record the current information before they can write new information.
In both cases, the administrative staff and the buyer's access to the bulletin board will need to be synchronized. Because one of the threads (the executive) has modified the shared resource (bulletin board). And we can see that the executive's workflow and the buyer's workflow (execution code) are completely different, but because they have access to the same variable shared resource (bulletin board), they need to synchronize.

Sync Lock

Before we talk about thread synchronization, let's look at how to synchronize threads.
The basic implementation of thread synchronization is still relatively easy to understand. We can add a lock to the shared resource, and this lock has only one key. Which thread acquires the key and has the right to access the shared resource.
Life, we may also encounter such an example. Some of the supermarkets offer automatic storage boxes outside. Each storage box has a lock, a key. People can use storage boxes with keys, put things in a storage box, lock the lockers, and take the keys away. In this way, the storage box is locked and no one else can access the storage box. (Of course, the real locker key can be taken away and copied, so don't put your valuables in the grocery store box.) So many supermarkets have adopted electronic password lock. )
Thread sync lock This model looks very intuitive. However, there is still a serious problem unresolved, where should this sync lock be added?
Of course, it was added to the shared resources. Fast-reacting readers are sure to be the first to answer.
Yes, if possible, we will certainly try to add the sync lock to the shared resource. Some more perfect shared resources, such as file system, database system, etc., have provided a relatively perfect synchronization lock mechanism. We don't have to lock these resources in addition, and these resources have locks on their own.
However, in most cases, the shared resources that we access in code are relatively simple shared objects. There's no place in these objects to lock us up.
Readers may make suggestions: Why not add a new area inside each object, specifically for lock-in? This design is certainly feasible in theory. The problem is that thread synchronization is not a common situation. If a lock space is created inside all objects because of this small probability event, there will be great space waste. Outweigh
As a result, modern programming languages are designed to add synchronous locks to code snippets. Specifically, the sync lock is added to the code snippet to access shared resources. It is important to remember that the sync lock is added to the code snippet.
The synchronization lock is added to the code snippet, which solves the above problem of space wasting in a good way. But it increases the complexity of the model and increases the difficulty of our comprehension.
Now let's analyze it carefully.sync Lock added on code snippet"The thread synchronization model.
First, we've solved the problem of where the sync lock is added. We have determined that the sync lock is not added to the shared resource but is added to the code snippet that accesses the shared resource.
Second, the problem we're trying to solve is what kind of lock we should add to the code snippet. This question is the focus of the focus. This is our particular concern: access to the same shared resources of different pieces of code, should be added to the same synchronization lock, if the addition of a different synchronization lock, then there is no synchronization function, there is no point.
This means that the synchronization lock itself must also be a shared object between multiple threads.

Synchronized keywords for the Java language

For a deeper understanding, take several examples of code snippet synchronization.
The synchronization lock model for different languages is the same. It's just a different way of expressing. Here we take the current most popular Java language as an example. The Java language uses synchronized keywords to lock code snippets. The entire grammatical form is expressed as

synchronized (Sync Lock) {   //

In particular, it is important to note that the sync lock itself must be a shared object.

... F1     () {new//    synchronized(lock1) {           //  Code Snippet A          //  access to shared resources         Resource1//    }}             

The above code doesn't make any sense. Because that synchronization lock is generated inside the function body. When each thread calls this code, a new synchronization lock is generated. A different synchronization lock is used between multiple threads. The purpose of synchronization is not achieved at all.

Synchronization code must be written in the form of the following, only meaningful.

 Public Static Final New Object ();.. F1 (     ) {synchronized///  Lock1 is a public sync           lock //  code Snippet a< c12/>//  access to shared resources         Resource1//    }

You don't have to declare a sync lock as static or public, but you must ensure that the same synchronization lock is used between the associated synchronization code.

In this case, you will wonder what this sync lock is. Why is it possible to declare a single Object object as a synchronous lock?
in Java, this is the concept of a synchronous lock. Any object reference can be used as a synchronous lock. We can understand the object reference as the memory address of the objects in the memory allocation system. So, to make sure that the same sync lock is used between the synchronization snippets, we need to ensure that the Synchronized keyword for these synchronized snippets uses the same object Reference, the same memory address. This is why I used the final keyword when I declared lock1 in the previous code, which is to ensure that the Lock1 object reference remains intact throughout the system.  
Some eager readers may want to continue to gain insight into the actual operating mechanism of synchronzied (synchronous lock). In the Java Virtual Machine specification (you can search with keywords like "JVM Spec" in Google), there is a detailed explanation of the synchronized keyword. Synchronized will be compiled into monitor enter, .... Monitor exit and other command pairs. Monitor is actually a synchronous lock. Each of the object reference conceptually corresponds to a monitor. &NBSP;
These implementation details are not the key to understanding the synchronization lock model. We continue to look at several examples to deepen our understanding of the synchronous lock model.  

 public  static  final  Object lock1 =  Object (); .... F1 () {  synchronized  (lock1) {//  Lock1 is a public sync lock  //  code snippet A //  access to shared resources Resource1  //  need to synchronize   
... f2 ()     {synchronized///  Lock1 is a public sync lock            //  code snippet B             access to shared resources         Resource1//    

In the code above, code snippet A and code snippet B are synchronous. Because they are using the same sync lock lock1.

If there are 10 threads executing code snippet A at the same time and 20 threads executing code fragment B at the same time, then the 30 threads will be synchronized between each other.  
These 30 threads compete for a synchronous lock lock1. At the same time, only one thread could take ownership of LOCK1, and only one thread could execute code snippet A or code snippet B. Other competing failed threads can only pause to run and go to the ready queue for that sync lock.  
Several thread queues are hung under each sync lock, including ready queues, call (Waiting) queues, and so on. For example, the lock1 corresponding ready queue can be called the Lock1-ready queue. There may be multiple threads running in each queue that are paused.  
Note that a thread that has failed to compete for a sync lock enters the ready queue for that synchronization lock, rather than the queue to be called (Waiting queue, which can also be translated as a wait queue) that you want to tell later. The threads in the ready queue are always ready to compete for a sync lock, ready to run at all times. The threads in the queue can only wait until a signal is notified before they can be transferred to the ready queue and ready to run.  
A thread that successfully acquires a synchronization lock, releasing the synchronization lock after the synchronization code snippet is executed. Other threads in the ready queue of the synchronization lock continue to compete for the next synchronization lock. The winner can continue to run, and the loser should stay in the ready queue.  
Therefore, thread synchronization is a very resource-intensive operation. We want to try to control the range of code snippets for thread synchronization. The smaller the range of code snippets you synchronize, the better. We use a noun " synchronize granularity  " to represent the scope of the synchronization snippet.  
synchronous granularity  
In the Java language, we can directly add the Synchronized keyword directly to the definition of the function.  
For example.  

synchronized ... F1 () {       //} This code is equivalent to ... F1 () {   synchronized(this// The Sync Lock is the     object itself //  

The same principle applies to static functions
Like what.

Static synchronized ...   F1 () {//} This code is equivalent to ... Static ... F1 ()   {synchronized//  Sync Lock is the class definition itself      //  

However, we should try to avoid this lazy practice of directly adding synchronized to the function definition. Because we want to control the granularity of synchronization. The smaller the code snippet, the better. The smaller the range of synchronized control, the better.
Not only do we have to work on narrowing the length of the synchronization code snippet, we also pay attention to segmenting the sync lock.
For example, the following code

 Public Static FinalObject Lock1 =NewObject (); .... F1 () {synchronized(LOCK1) {//Lock1 is a public sync lock//Code Snippet A//Access shared resources Resource1//need to synchronize}} ... f2 () {synchronized(LOCK1) {//Lock1 is a public sync lock//Code Snippet B//Access shared resources Resource1//need to synchronize}} ... f3 () {synchronized(LOCK1) {//Lock1 is a public sync lock//Code Snippet C//Access shared resources Resource2//need to synchronize}} ... f4 () {synchronized(LOCK1) {//Lock1 is a public sync lock//Code Snippet D//Access shared resources Resource2//need to synchronize    } } 

The 4-segment synchronization code above uses the same sync lock lock1. All threads that call any piece of code in the 4-segment code need to compete for the same sync lock lock1.
We analyzed it carefully and found that it was not necessary.
Because the F1 () code snippet A and F2 () of code snippet B accesses the shared resource that is RESOURCE1,F3 () of the code snippet C and F4 () of the code snippet D access to the shared resource is resource2, they are not necessarily competing for the same sync lock lock1. We can add a sync lock lock2. The Code for F3 () and F4 () can be modified to:

 Public Static FinalObject Lock2 =NewObject (); .... f3 () {synchronized(LOCK2) {//Lock2 is a public sync lock//Code Snippet C//Access shared resources Resource2//need to synchronize}} ... f4 () {synchronized(LOCK2) {//Lock2 is a public sync lock//Code Snippet D//Access shared resources Resource2//need to synchronize    } } 

In this way, F1 () and F2 () will compete for Lock1, while F3 () and F4 () will compete for Lock2. Thus, separately competing two locks, can greatly reduce the probability of the synchronization lock competition, thus reducing the system overhead.

Signal Volume

The synchronous lock model is just the simplest synchronization model. At the same time, only one thread can run synchronization code.
Sometimes, we want to deal with more complex synchronization models, such as producer/consumer models, read-write synchronization models, and so on. In this case, the synchronous lock model is not enough. We need a new model. This is the semaphore model we're going to tell.
The semaphore model works as follows: While the thread is running, it can actively stop and wait for a semaphore to be notified, when the thread enters the waiting queue of that semaphore, and then continues running after the notification.
In many languages, a synchronous lock is represented by a specific object, and the object name is usually called Monitor.
Similarly, in many languages, semaphores are usually represented by a specific object name, such as Mutex,semphore.
The semaphore model is much more complex than the synchronous lock model. In some systems, the semaphore can even be synchronized across processes. Other semaphores even have a count function that controls the number of threads running concurrently.
There is no need for us to consider such a complex model. All of those complex models are derived from the most basic models. As long as you have mastered the most basic semaphore model-"wait/notice" model, the complex model will be solved.
Let's take the Java language as an example. The Java language inside the synchronization lock and semaphore concept are very vague, there is no special object noun to represent the synchronization lock and semaphore, only two sync lock related keywords--volatile and synchronized.
This ambiguity, although the concept is unclear, but also avoids the monitor, Mutex, semphore and other nouns brought about by various misunderstandings. We don't have to stick to the argument of the noun, we can focus on understanding the actual operating principle.
In the Java language, any object reference can be used as a synchronous lock. Similarly, any object reference can also be used as a semaphore.
The wait () method for object objects is to wait for notification, and the Notify () method of object objects is to give notice.
The specific invocation method is
(1) Waiting for notification of a semaphore

 Public Static FinalObject signal =NewObject (); .... F1 () {synchronized(Singal) {//first we need to get this semaphore. This signal volume is also a synchronous lock//only after the successful acquisition of the signal and the synchronous lock can we enter this codeSignal.wait ();//this is to give up the semaphore. This thread is going to go to the call (waiting) queue of the signal semaphore//poor. The amount of hard-earned signal that was given up.//after the notification, go from the call (waiting) queue to the ready queue//go to the ready queue and take a step closer to the CPU core and have the opportunity to continue with the following code. //It is still necessary to compete with the signal Sync lock in order to actually continue executing the code below. Commiseration AH. ...} } 

It is important to note that the signal.wait () in the above code means. Signal.wait () can easily lead to misunderstanding. Signal.wait () does not mean that signal begins to wait, but rather that the current thread that runs this code begins to wait for the signal object, which is the call (waiting) queue that enters the signal object.

(2) A notification that a semaphore is emitted ... f2 () {synchronized(Singal) {//First, we also want to get this semaphore.     It is also a synchronous lock. //only after the successful acquisition of the signal and the synchronous lock can we enter this codeSignal.notify ();//here, we notify signal of a thread in the queue to be called. //If a thread waits for this notification, that thread will go to the ready queue//However, this thread still continues to have signal this synchronization lock, and this thread continues to execute//Hey, although this thread kindly notifies other threads,//However, this thread may not be so sharp sense, giving up the sync lock//This thread continues to execute the following code...} } 

It should be noted that the meaning of signal.notify (). Signal.notify () is not informing signal of the object itself. Instead, it notifies other threads that are waiting for the signal semaphore.

These are the basic uses of the Wait () and notify () object.
In fact, wait () can also define the waiting time, and when the thread is in the queue for a semaphore, wait for a long enough time, wait, wait, and then move from the queue to the ready queue.
In addition, there is a notifyall () method that indicates that all threads in the queue are notified of the call.
These details do not have an impact on the overall situation.

Green Thread

Green thread is a concept that is relative to the operating system thread (Native thread).
The operating system thread (Native thread) means that the threads inside the program are actually mapped to the operating system's threads, and the threads are run and dispatched by the operating system.
Green thread means that the threads inside the program do not really map to the threads of the operating system, but rather are dispatched by the language runtime itself.
Threads in the current version of the Python language can be mapped to operating system threads. Threads in the current version of the Ruby language are green threads that cannot be mapped to the operating system's threads, so the Ruby language thread runs slower.
Does it say that green threads are slower than operating system threads? Of course not. In fact, the situation may be the opposite. Ruby is a special example. The thread scheduler is not very mature.
Currently, thread's popular implementation model is green thread. For example, Stackless Python introduces a more lightweight concept of green threading. In terms of thread concurrency programming, both speed and concurrency are better than python.
Another more notable example is Erlang (an open source language developed by Ericsson).
Erlang's green threading concept is very thorough. Erlang's thread is not called thread, but is called process. This is easily confused with the process. Be careful to differentiate here.
There is no need for synchronization between ErLang process. Because all variables in the Erlang language are final, the value of the variable is not allowed to change. Therefore, there is no need to synchronize at all.
Another benefit of the final variable is that there is no cross-reference between objects, and it is not possible to make a ring-shaped association, and the association between objects is unidirectional and tree-like. As a result, the algorithm for memory garbage collection is also highly efficient. This allows Erlang to achieve the soft real time (soft realtime) effect. This is not an easy task for a language that supports memory garbage collection.

"Summary" Java thread synchronization mechanism deeply elaborated

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.