To use an analogy: an object is like a big house, the door is always open. There are many rooms (aka methods) in a house. These rooms are either locked (synchronized method) or unlocked (normal method). There is a key at the door of the room. This key can open all locked rooms. In addition, I compare all threads that want to call methods of this object to people who want to enter a certain room in this house. That’s all there is to it, let’s take a look at how these things work with each other.
Here we first clarify our prerequisites. The object has at least one synchronized method, otherwise this key has no meaning. Of course, there will be no such theme of ours.
A person wants to enter a locked room. He comes to the door of the house and sees the key there (indicating that no one else wants to use the locked room yet). So he went up, got the keys, and used the rooms as he planned. Note that he returns the key immediately after each use of the locked room. Even if he wants to use two locked rooms in a row, he has to return the key and get it back in between.
Therefore, the principle of using keys under normal circumstances is: "Borrow them as you use them, and return them as soon as you use them."
At this time, other people can use the unlocked rooms without restriction. One person can use one room, or two people can use one room. There are no restrictions. But if someone wants to enter a locked room, he has to run to the door and look. If you have the key, of course you can take it and leave. If you don't have the key, you can only wait.
If many people are waiting for the key, who will get it first when the key is returned? Not guaranteed. Like the guy in the previous example who wants to use two locked rooms in a row, if there are other people waiting for the key when he returns the key, there is no guarantee that this guy can get it again. (The JAVA specification clearly states in many places that there are no guarantees, such as how long it will take for Thread.sleep() to return to running after a rest, which thread with the same priority will be executed first, and when the lock to access the object is released, multiple threads in the waiting pool will Which thread will get priority, etc. I think the final decision lies with the JVM. The reason why there is no guarantee is because when the JVM makes the above decision, it does not simply make a judgment based on a condition, but According to many conditions. If there are too many judgment conditions, it may affect the promotion of JAVA. It may be due to the reason of intellectual property protection. But I believe that these are not unreasonable. Determined, not completely uncertain, because the computer itself runs according to instructions. Even if it seems to be random, there are actually rules to be found. Anyone who has studied computers knows that the scientific name for random numbers in computers is pseudo-random. Numbers are written by people using a certain method, and they appear to be random. In addition, it may be because it is too troublesome and meaningless to determine, so if you are not sure, you are not sure.)
Let’s look at synchronized code blocks again. It is slightly different from the synchronization method.
1. In terms of size, synchronized code blocks are smaller than synchronized methods. You can think of a synchronized code block as a space in an unlocked room separated by a locked screen.
2. The synchronization code block can also manually specify the key of another object. It's like specifying which key can be used to open the lock of this screen. You can use the key of this house; you can also specify the key of another house to open it. In this case, you have to run to the other house to open the lock. Get that key and use that house key to open the locked screen of this house.
Remember that the key to another house you obtained does not prevent others from entering the unlocked rooms in that house.
Why use synchronized code blocks? I think it should be like this: First of all, the synchronization part of the program affects the operating efficiency, and a method usually creates some local variables first, and then performs some operations on these variables, such as calculations, display, etc.; and synchronization covers The more code there is, the more serious the impact on efficiency. So we usually try to keep its scope of impact as small as possible. How to do it? Synchronized code blocks. We only synchronize the parts of a method that should be synchronized, such as operations.
In addition, the synchronization code block can specify the key. This feature has the additional benefit of occupying the key of an object within a certain period of time. Remember what I said before about the principles of using keys under normal circumstances. These are no ordinary circumstances. The key you obtain is not returned forever, but only returned when you exit the synchronized code block.
Let’s use the analogy of the guy earlier who wanted to use two locked rooms in a row. How can I continue to use another room after using one room? Use synchronized code blocks. First create another thread, make a synchronization code block, and point the lock of that code block to the key of the house. Then start that thread. As long as you can grab the key to the house when you enter that code block, you can keep it until you exit that code block. In other words, you can even traverse all the locked rooms in this room, and even sleep (10*60*1000), but there are still 1000 threads waiting for the key at the door. It's quite enjoyable.
Let’s talk about the correlation between the sleep() method and the key. If a thread is forced to sleep() after getting the key and has not completed the synchronization content, the key will still be there. The key will not be returned until it is run again and all synchronization content is completed. Remember, the guy just got tired from working and went to take a rest. He didn't finish what he wanted to do. In order to prevent others from entering the room and making a mess inside, he had to wear the only key on his body even when sleeping.
Finally, someone may ask, why do we need one key to open each door, instead of one key for each door? I think this is purely a matter of complexity. One key for one door is certainly safer, but it will involve a lot of problems. The generation, storage, acquisition, return of keys, etc. Its complexity may increase geometrically as the number of synchronization methods increases, seriously affecting efficiency. This can be regarded as a matter of trade-off. How undesirable it is to greatly reduce efficiency in order to increase a little bit of security.