I suddenly found that there are still many things that I need to understand, such as the usage of the key word synchronized. Because when I was doing research on creating connection pool sockets yesterday, I found that if I didn’t understand this concept, I wouldn’t be able to do it at all. To continue, so I decided to cool down my interest in Socket for a while, and look back at synchronized.
After reading Think in java all morning, I feel that it is still very effective, and I should write it down immediately to deepen my impression. I feel that my The reusability of the brain is extremely low. It always needs to generate new memory objects, which consumes a lot of repetitive work. Therefore, the more similar tasks such as recording, analysis, and summary should be done.
To understand the usage of synchronized, you must first know what it is. What problem is it used to solve? Since synchronized means synchronization, then of course it is used to solve the problem of out-of-synchronization. Here is an example of out-of-synchronization to demonstrate possible problems.
In this example, we will Create two thread classes. One is called TwoCounter. Its job is to accumulate two counter variables at the same time. Starting from 1, you will immediately think that we are going to use it to achieve synchronization. The other object is called Watcher. As the name suggests, It is used for monitoring work. It is responsible for checking whether the values of the two counters in the TwoCounter thread are equal. It seems that this is meaningless work, because since it is accumulated synchronously, how can the values of the two counters be different? What about equality??
But, this is not the case. Let’s look at the program first. Before looking at this program, it is best to read 14.2.1 of Think in Java. My program is actually based on the instructions given in that section. The example shown is simplified, and the main class is changed to Sharing2
class TwoCounter extends Thread {
PRivate int count1 = 0, count2 = 0;
private boolean started=false;
public void start(){
if (!started ) file://Prevent multiple calls to the Start method on a thread
{
started=true;
super.start();
}
}
public void run() {
while (true) {
count1++;
file ://If TwoCounter runs to this point and the CPU time slice is allocated to Watcher, then of course the values of the two counters read by Watcher at this time will be different. This possibility exists. "This is due to the nature of threads - they can be suspended (paused) at any time. So between the execution times of the above two lines, sometimes there will be a pause in execution. At the same time, the Watcher thread will also follow in, And the comparison is performed at this time, causing the counters to be unequal." (Think in Java)
count2++;
System.out.println("Count1="+count1+",Count2="+count2);
try {
sleep(500);
} catch (InterruptedException e){}
}
}
public void synchTest() {
Sharing2.incrementaccess();
if(count1 != count2)
System.out.println(" Unsynched");//Once out of sync is found, it will be displayed immediately
}
}
class Watcher extends Thread {
private Sharing2 p;
public Watcher(Sharing2 p) {
this.p = p;
start();
}
public void run() {
while(true) {
p.s.synchTest();
try {
sleep(500);
} catch (InterruptedException e){}
}
}
}
public class Sharing2 {
TwoCounter s;
private static int accessCount = 0;
public static void incrementAccess() {
accessCount++;
System.out.println("accessCount="+accessCount);
}
public static void main(String[] args ) {
Sharing2 aaa = new Sharing2();
aaa.s=new TwoCounter();
aaa.s.start();//Open the TwoCounter thread
new Watcher(aaa);//Open the Watcher thread
}
}
The above comments make it very clear that out-of-synchronization may occur. But the strange thing is that when I run it, I never encounter out-of-synchronization. So there is only one situation, that is, in the program count1++ and count2++ are almost executed at the same time, and the watcher thread cannot be inserted. But why is there definitely a desynchronization after the program on Think in Java is run? The principles of the two programs are exactly the same, the only difference is mine The program is relatively simple and runs under the command line without using a GUI. Is it because running in Applet mode or running in the Windows main window is more expensive, giving the watcher an opportunity to take advantage of it? So I tried to use count1++ and count2++. A loop statement is added in between to artificially increase the gap. The purpose is to allow the watcher to insert in, resulting in the monitored count1 not equal to count2, resulting in desynchronization. The modified program is as follows
.... ..
count1++;
for(int i=0;i<5000;i++);
count2++;
......
OK! Run the program again, and soon there will be desynchronization. It seems that It confirms that my analysis just now is correct. But the strange thing is that after Unsynchronized was output once, it never appeared again. In other words, the watcher thread only detected that the two counters had different counts once. This made me feel a little depressed. , is it a coincidence or necessity? Maybe the time is too short, there will definitely be Unsynchronized output after waiting.
Forget it, let’s put this problem aside for now, let’s continue.
Since the problem of out-of-synchronization has arisen, it is obvious that the solution is synchronized: change the run method and SynchTest method of TwoCounter into synchronized methods. What does this mean? What are the benefits? Please refer to Think in Java. Section 14.2.2 has a very detailed and thorough explanation. Especially the concept of monitors, which we usually call object locks, is explained very clearly in the book.
In short, the code that needs to be modified is as follows:
class TwoCounter extends Thread {
public synchronized void run() {
while (true) {
count1++;
count2++;
System.out.println("Count1="+count1+",Count2="+count2);
try {
sleep(500);
} catch (InterruptedException e){}
}
}
public synchronized void synchTest() {
Sharing2.incrementAccess();
if(count1 != count2)
System.out.println(" Unsynched");//Once out of sync is found, it will be displayed immediately
}
}
Omit the rest, indicating that it is actually very simple to solve the problem, haha.
We note that both run() and synchTest() are "Synchronous". If only one of the methods is synchronized, the other is free to ignore the object's lock and be called without hindrance. So you must remember an important rule: all methods that access a critical shared resource must be set to synchronized, otherwise they will not work properly.
Now I have encountered a new problem. Watcher2 can never see what is going on because the entire run() method is set to "synchronous". And since run() must be run for each object, the lock can never be opened and synchTest() never gets called. You can see this result because accessCount has not changed at all.
To solve this problem, one way we can take is to isolate only part of the code in run(). The part of code you want to isolate in this way is called a "critical area," and the synchronized keyword must be used in different ways to set a critical area. Java provides support for critical areas through "synchronized blocks"; this time, we use the synchronized keyword to indicate that the object's lock is used to synchronize the code enclosed within it. As shown below:
synchronized(syncObject) {
// This code can be accessed by only
// one thread at a time, assuming all
// threads respect syncObject′s lock
}
can enter the synchronization block Previously, a lock had to be obtained on synchObject. If another thread has acquired the lock, the block cannot enter and must wait for the lock to be released.
You can delete the synchronized keyword from the entire run() and replace it with a synchronized block surrounding the two keyword lines to complete the modification to the Sharing2 example. But what object should be used as a lock? That object has been marked by synchTest()?? That is the current object (this)! So the modified run() method looks like this:
file:// Note that there is no synchronized keyword
public void run() {
while (true) {
synchronized(this){
count1++;
count2++;
}
System.out.println("Count1="+count1+",Count2="+count2);
try {
sleep(500);
} catch (InterruptedException e){}
}
}
file: //Note that synchTest() still needs the synchronized keyword. Consider why
In this case, the synchTest method can be called, and we can also see the change in accessCount.
The above is a memo for beginners learning Java (7). For more related content, please pay attention to the PHP Chinese website (m.sbmmt.com)!