Java basic knowledge traps (9) and java basic knowledge traps
This article is published on my blog.
Today I want to talk about JAVA multithreading. If you have any errors, please point them out. As we all know, JAVA processing on the server also has great advantages. Many companies also run JAVA processes on the server, this shows that JAVA has some advantages in processing this multi-thread and concurrency (this is a bit pitfall ). Next let's take a look
In java, PV signals like operating systems are not directly operated. However, synchronized is provided to implement synchronization, which may not be rigorous. The Basic Class Object in JAVA includes the following methods:
public final native void notify();public final native void notifyAll();public final native void wait(long timeout) throws InterruptedException;
Any method that inherits and implements Object objects has a memory lock. When a thread obtains the modified Memory Lock, other threads cannot access the modified memory and wait for it to be transferred, I know that the thread that uses the modified Memory Lock can only release the Memory Lock. Here, there are many places on the internet, just to put it simply, and now I know it better: for the first time, when a thread obtains the Memory Lock of the modified object, this thread enters the lock pool. Other threads that need to use this object enter the waiting pool (wait () is called instantly ()). I know that the thread that uses the modified Object Memory Lock calls y () or calls notifyAll () to release it. It seems that there is another timeout condition. Now I don't know how to look back !! The system will randomly select a thread from the waiting pool to occupy the Memory Lock of the object and enter the lock pool. Note that Class is also an object, it is not the object that we generally think of as new. Here it also belongs to the object, so there is a situation where the entire object is locked and the entire class is locked!
The above three methods must be used with the synchronized keyword, because wait () and Policy () can be operated only when the object and class lock are obtained, to enter the waiting pool and release the lock.
The Spring Festival is approaching. The following is an example of getting a Train ticket. Let's take a look at the Train class first:
Class Train {/*** first-class advanced agents */private int Tickets = 10;/*** buy a Train ticket ** @ throws Exception */public void Purchase (String name) throws Exception {// simulate the Thread for obtaining Tickets data. sleep (50); if (Tickets> 0) {// simulate operations such as acquiring order data Thread. sleep (10); int temp = Tickets --; System. out. println (name + ":" + temp); // simulate operations such as generating orders Thread. sleep (10);} else {System. out. println (name + "failed to grab the ticket! ");}}}
TrainThread class:
class TrainThread extends Thread{ private Train train; public TrainThread(Train train){ this.train = train; } @Override public void run() { try { this.train.Purchase(Thread.currentThread().getName()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
Main function:
Public static void main (String [] args) {// example of buying a Train ticket during the Spring Festival, simulating 10 train Train = new Train () for 100 people (); trainThread [] threads = new TrainThread [100]; for (int I = 0; I <100; I ++) {TrainThread th1 = new TrainThread (train ); threads [I] = th1;} for (int I = 100-1; I> = 0; I --) {threads [I]. start ();}}
Execution result output:
Thread-99: 10Thread-97: 9Thread-81: 1Thread-79: 0Thread-25 failed to get the ticket! Thread-27 ticket snatching failed! Thread-38 failed to grab the ticket! Thread-98:-1Thread-96:-2Thread-40: failed to get the ticket! Thread-84:-3
No, the-1 ticket is out and there is no need to implement it. In the analysis, it should be caused by 10 milliseconds of sleep in if (Tickets> 0, it is because when sleep for 10 milliseconds, many threads also come in and we have determined if (Tickets> 0). Since we have analyzed the causes, what are the solutions? Of course, we can lock this code block. When a thread is judging to operate the Tickets variable, other threads are not allowed to access it. There is a keyword synchronized in Java. Let's take a look at the Code:
Public void Purchase (String name) throws Exception {// simulate the Thread for obtaining Tickets data. sleep (50); synchronized (this) {if (Tickets> 0) {// simulate operations such as acquiring order data Thread. sleep (10); int temp = Tickets --; System. out. println (name + ":" + temp); // simulate operations such as generating orders Thread. sleep (10);} else {// System. out. println (name + "failed to grab the ticket! ");}}}}
Now let's look at the output:
Thread-93: 10Thread-5: 9Thread-3: 8Thread-1: 7Thread-4: 6Thread-2: 5Thread-0: 4Thread-13: 3Thread-18: 2Thread-16: 1
If you run it several times and find that there are no duplicates, there will be no negative numbers. It is normal! Further analysis, the Code uses synchronized (this): After the synchronized statement block ends, the lock is automatically released so that one of the threads in the wait pool can obtain the lock. Note the differences between sleep and notify: the two most important differences are that sleep releases CPU control but releases the Memory Lock to release the Memory Lock, while policy also releases the Memory Lock. In fact, there is another method to use synchronized to lock objects. You can modify the Code as follows:
Public synchronized void Purchase (String name) throws Exception {// simulate the Thread for obtaining Tickets data. sleep (50); if (Tickets> 0) {// simulate operations such as acquiring order data Thread. sleep (10); int temp = Tickets --; System. out. println (name + ":" + temp); // simulate operations such as generating orders Thread. sleep (10);} else {// System. out. println (name + "failed to grab the ticket! ");}}
Everyone will think that this is easier to use. It is simple, but it may take into account performance issues. If this object is still static, it will be equivalent to synchronized (class) for a long time, this greatly reduces the system running speed. As mentioned above, not only do objects have memory locks, but even classes have memory locks!
It does not mean that there are two methods to implement multithreading. One is to inherit the Thread, and the other is to implement the interface Runnable. Yes. Here we change Train to TrainRun and implement the Runnable interface. The Code is as follows:
Class TrainRun implements Runnable {/*** first-class high-level agent */private int Tickets = 10; private Object obj = new Object (); /*** buy a train ticket ** @ throws Exception */public synchronized void Purchase (String name) throws Exception {Thread. sleep (50); if (Tickets> 0) {Thread. sleep (10); int temp = Tickets --; System. out. println (name + "grab:" + temp); Thread. sleep (10);} else {}}@ Override public void run () {try {this. purchase (Thread. currentThread (). getName ();} catch (Exception e) {e. printStackTrace ();}}}
Main function:
TrainRun train = new TrainRun(); Thread[] threads = new Thread[100]; for (int i = 0; i < 100; i++) { Thread th1 = new Thread(train); threads[i] = th1; } for (int i = 100 - 1; i >= 0; i--) { threads[i].start(); }
The output results also meet the requirements:
Thread-99: 10Thread-0: 9Thread-2: 8Thread-4: 7Thread-6: 6Thread-8: 5Thread-10: 4Thread-12: 3thread14: 2Thread-16: 1
I didn't mean to use the wait and notify methods above. I'm glad to say that these three functions are mainly used to wake up threads, in the above example, it is useless to wake up and coordinate each other to accomplish one thing, but let's look at the example below:
An aunt is responsible for transmitting dishes and chopsticks, while an aunt is responsible for washing dishes. Each time she washes dishes, the basin can accommodate a maximum of five bowls, and the code is used to describe the work process of the two aunts, first, an aunt is responsible for transferring the dishes of an aunt. Two threads are used, and the bowl in the basin is shared and used, if there is no bowl in the basin, the delivery will be completed. If the number of bowls in the basin exceeds 5 During the transfer, the washing will be completed. Check the Code:
Conf configuration class:
Class Conf {public static int passCount = 2; // The number of times the aunt uploads the chopsticks public static int COUNT = 2; // The number of times the aunt washes the chopsticks public static int couter = 5; // make a basin of 5 large capacity}
Work class:
Class Work {public int count = 0;/*** */public synchronized void Wash () {if (count <1) {try {wait ();} catch (InterruptedException e) {e. printStackTrace () ;}} System. out. println ("\ t has chopsticks:" + count + ""); System. out. println ("\ t washing chopsticks:" + count + ""); count --; System. out. println ("\ t remaining chopsticks:" + count + ""); y ();}/*** transfer chopsticks */public synchronized void Pass () {if (count> = Conf. couter) {try {wait ();} catch (InterruptedException e) {e. printStackTrace () ;}} System. out. println ("\ t has chopsticks:" + count + ""); System. out. println ("\ t to upload another chopsticks"); count ++; System. out. println ("\ t transfer completed"); y ();}}
Transmission aunt PassAunt class:
class PassAunt extends Thread{ private Work work ; public PassAunt(Work work){ this.work = work; } @Override public void run() { for (int i = 0; i < Conf.passCount; i++) { this.work.Pass(); try {Thread.sleep(50); } catch (InterruptedException e) {e.printStackTrace();} } }}
Aunt WashAunt class:
class WashAunt extends Thread{ private Work work ; public WashAunt(Work work){ this.work = work; } @Override public void run() { for (int i = 0; i < Conf.washCount; i++) { this.work.Wash(); try {Thread.sleep(50); } catch (InterruptedException e) {e.printStackTrace();} } }}
Main function:
public static void main(String[] args) { Work work = new Work(); PassAunt passThread = new PassAunt(work); WashAunt washThread = new WashAunt(work); passThread.start(); washThread.start(); }
The result is as follows:
There are already dishes and chopsticks: 0 and then transfer one more. After the transfer is completed, there are already dishes and chopsticks: 1 is washing dishes and chopsticks: 1st are left with chopsticks: 0 are already using chopsticks: 0 and then transfer another chopsticks. After transfer, there are already some chopsticks: 1 washing chopsticks: 1st remaining chopsticks: 0
We can see that two washes are passed in the output to meet the conditions. If there are a large number of guests and the boss also joins the delivery bowl chopsticks team, now let's see the call:
Public static void main (String [] args) {Work work Work = new work (); PassAunt passThread = new PassAunt (work); PassAunt passThread01 = new PassAunt (Work ); washAunt failed thread = new WashAunt (work); passThread. start (); passThread01.start (); threads thread. start ();} class Conf {public static int passCount = 2; // number of times that Aunt uploads chopsticks public static int limit count = 4; // number of times for aunt to wash chopsticks public static int couter = 5; // The number of large-capacity POTS is 5}
Run multiple outputs:
There are already chopsticks: 2 are washing chopsticks: 2nd are remaining chopsticks: 1 have already had chopsticks: 1 are washing chopsticks: 1st are remaining chopsticks: 0
From this we can see that we have added a passing aunt and modified the number of dishwashing times. The result is normal. Then let's modify passCount to think that 4 and coupon count are 2. Let's see how the results of a single aunt wash the dishes!
Class Conf {public static int passCount = 4; // number of times that aunt sent chopsticks public static int COUNT = 2; // number of times that aunt sent chopsticks public static int couter = 5; // make a basin of 5 large capacity}
public static void main(String[] args) { Work work = new Work(); PassAunt passThread = new PassAunt(work); WashAunt washThread = new WashAunt(work); WashAunt washThread01 = new WashAunt(work); passThread.start(); washThread01.start(); washThread.start(); }
The output result may be:
There are already chopsticks: 0 are washing chopsticks: 0th are left with chopsticks:-1 have already had chopsticks:-1 and then transfer another chopsticks.
It is obviously wrong to have-1 bowl. It is correct to say that the number of aunts who deliver the dishes is correct. Now let's see where there is a problem with the method of washing dishes. The current thread calls the wait method and wakes up again to continue the execution. At this time, I didn't judge how many bowls are in the basin. Modify and use the while:
Public synchronized void Wash () {while (count <1) {try {wait ();} catch (InterruptedException e) {e. printStackTrace () ;}} System. out. println ("\ t has chopsticks:" + count + ""); System. out. println ("\ t washing chopsticks:" + count + ""); count --; System. out. println ("\ t remaining chopsticks:" + count + ""); y ();}
Set the number of dishes as needed:
Public static int COUNT = 2; // number of times that aunt sent chopsticks
Run the command again to view the result:
There are already dishes and chopsticks: 0, and then transfer one more. After transfer, there are already dishes and chopsticks: 1 washing chopsticks: 1st remaining dishes and chopsticks: 0
Run it multiple times. The dishes are all washed! The dishes are done. Will there be errors in passing the dishes? Next, let's take a look at the addition of an aunt passing the dishes and the aunt who washed the dishes. Let's look at the Code:
Class Conf {public static int passCount = 2; // number of times that aunt sent the chopsticks public static int COUNT = 2; // number of times that aunt sent the chopsticks public static int couter = 5; // make a basin of 5 large capacity}
public static void main(String[] args) { Work work = new Work(); PassAunt passThread = new PassAunt(work); PassAunt passThread01 = new PassAunt(work); WashAunt washThread = new WashAunt(work); WashAunt washThread01 = new WashAunt(work); passThread.start(); washThread01.start(); passThread01.start(); washThread.start(); }
Output result:
There are already dishes and chopsticks: 0, and then transfer one more. After transfer, there are already dishes and chopsticks: 1 washing chopsticks: 1st remaining dishes and chopsticks: 0
Set passCount to 4 and merge count to 2 again. The result is as follows:
There are already chopsticks: Two Have another chopsticks, and the transfer has been completed.
At the end of the day, there were still chopsticks and they were washed. After analysis, this was because two aunts who passed the chopsticks sent a total of eight chopsticks, and the total number of times the two aunts washed the dishes was 4, so it is correct to leave four dishes unwashed and leave four for analysis and verification results! Then modify the variable value:
Public static int passCount = 4; // number of times that aunt sent chopsticks public static int COUNT = 4; // number of times that aunt sent chopsticks public static int couter = 5; // The number of large-capacity five pots
The result may be:
There are already chopsticks: 2 are washing chopsticks: 2nd are remaining chopsticks: 1 have already had chopsticks: 1 are washing chopsticks: 1st are remaining chopsticks: 0
It indicates that the last dishwashing is correct. Here, we may all find that this operation is affected by passCount and distinct count. We can remove the suitable washing aunt and wash it. If tens of thousands of aunts are transferred, we will not pass this requirement. Of course we can, see the following code:
Modify configuration Conf class
Class Conf {public static int couter = 5; // Large Capacity of the basin: 5}
Modify the Work class:
Class Work {// The above is omitted as before./*** transfer tableware */public synchronized void Pass () {while (count> = Conf. couter) {try {wait ();} catch (InterruptedException e) {e. printStackTrace () ;}} System. out. println ("\ t has chopsticks:" + count + ""); System. out. println ("\ t to upload another chopsticks"); count ++; System. out. println ("\ t transfer completed"); y ();}}
Then modify the PassAunt class:
Class PassAunt extends Thread {// previously omitted @ Override public void run () {while (true) {this. work. pass (); try {Thread. sleep (50);} catch (InterruptedException e) {e. printStackTrace ();}}}}
Then modify the WashAunt class:
Class WashAunt extends Thread {// previously omitted @ Override public void run () {while (true) {this. work. wash (); try {Thread. sleep (50);} catch (InterruptedException e) {e. printStackTrace ();}}}}
Then modify the mian function:
public static void main(String[] args) { Work work = new Work(); PassAunt passThread = new PassAunt(work); PassAunt passThread01 = new PassAunt(work); PassAunt passThread02 = new PassAunt(work); PassAunt passThread03 = new PassAunt(work); WashAunt washThread = new WashAunt(work); WashAunt washThread01 = new WashAunt(work); passThread.start(); washThread01.start(); passThread01.start(); passThread02.start(); passThread03.start(); washThread.start(); }
In fact, it is completely correct to change if to while and change the for loop to while. After multiple running operations, the results will not show negative numbers or the number of more than five chopsticks. Test again. Check whether the results are normal if there are more aunts or aunts who send chopsticks to the new user.
First come here this time. Keep recording!