π§‘πππππ€ππ§‘πππππ€π
Thank you for your support and encouragement. It's not easy to make
π Ask for some praise π β Collection β β follow β
Get up with one button three times!
π§‘πππππ€π π§‘πππππ€π
1, Overview of thread safety
If there are multiple threads running at the same time, and these threads may run the same piece of code at the same time. Each time the program runs, the result is the same as that of a single thread, and the values of other variables are the same as expected, so it is thread safe.
2, Thread synchronization mechanism
When we use multiple threads to access the same resource, and multiple threads have write operations on the resource, thread safety problems are easy to occur. To solve the security problem of concurrent access to a resource by multiple processes, Java provides a synchronized mechanism to solve it.
There are three methods to realize synchronous operation in Java: synchronous code block, synchronous method and locking mechanism.
Case, using multithreading to simulate cinema ticket sales, selling 100 tickets, with three ticket windows
//Multithreading is realized by implementing the Runnable interface public class RunnableImp implements Runnable { //Set the total number of votes, 100 tickets private int ticket=100; //Rewrite the run method, set the thread task, and sell tickets @Override public void run() { //Dead cycle, repeat ticket sales while (true){ //Check whether the tickets are sold out if (ticket>0){ //Not sold out. Keep selling tickets System.out.println(Thread.currentThread().getName()+"--Selling the third"+ticket+"Ticket"); ticket--; } } } }
//Simulated ticket selling public class test { public static void main(String[] args) { RunnableImp runnableImp = new RunnableImp(); Thread thread = new Thread(runnableImp); thread.start();//Window 1 new Thread(runnableImp).start();//Window 2 new Thread(runnableImp).start();//Window 3 } }
Test results:
Thread safety issue 1: sell the same ticket
Thread safety issue 2: selling tickets that don't exist
1. Synchronous code block
Use format: synchronize(Lock object){ //Thread safe code blocks may occur } be careful: 1. The lock object can be any data type 2. Multiple thread objects should use the same lock Function of lock object: Lock the synchronization code block and let only one thread execute in the synchronization code block
Sample code;
//Multithreading is realized by implementing the Runnable interface public class RunnableImp implements Runnable { //Set the total number of votes, 100 tickets private int ticket=100; //Create an object Object obj=new Object(); //Rewrite the run method, set the thread task, and sell tickets @Override public void run() { //Dead cycle, repeat ticket sales while (true){ //Set synchronization code block synchronized (obj){ //Check whether the tickets are sold out if (ticket>0){ //Not sold out. Keep selling tickets System.out.println(Thread.currentThread().getName()+"--Selling the third"+ticket+"Ticket"); ticket--; } } } } }
Run the test class to get the test results:
From the test results, it can be found that the thread safety problem of selling duplicate tickets and non-existent tickets is solved.
Principle analysis of synchronization technology realized by synchronous code block:
A lock object is used in the synchronization code block, which is also called ("synchronization lock", "object lock" and "object monitor"). In the case of multi-threaded ticket selling, three threads seize the execution right of the CPU together. Which thread grabs the execution right of the CPU will execute the run() method. When the synchronized code block is encountered, It will check whether there is a lock object in the code block. If so, it will obtain the object and enter the code block for execution; If the remaining threads also grab the execution right of the CPU, they will also execute the synchronized synchronization code block and judge whether there is a lock object. If no lock object is found, the thread will enter the blocking state until the thread that obtains the lock object returns the lock object, That is, the thread cannot enter the synchronization and execute the code until the previous thread executes the code in the synchronization code block and returns the lock object to the synchronization code block.
Therefore, the core of synchronization technology is that the thread in synchronization will not release the lock object until it is executed; If the thread outside the synchronization has no lock, the object cannot be synchronized
2. Synchronization method
Two solutions to thread safety:Use synchronization method effect: The synchronization method will lock the code inside the method and let only one thread use it. Use steps: 1.Extract the code that accesses the shared data, that is, the code that may have thread safety problems, Put it in a way 2.Add to method synchronized Modified hunting Use format: Permission modifier synchronized Return value type method name(parameter list){ Code that may cause thread safety problems(Code block that accessed the shared data) }
Example code:
//Multithreading is realized by implementing the Runnable interface public class RunnableImp implements Runnable { //Set the total number of votes, 100 tickets private int ticket=100; //Rewrite the run method, set the thread task, and sell tickets @Override public void run() { //Dead cycle, repeat ticket sales while (true){ sellticket(); } } //Use synchronization method public synchronized void sellticket(){ //Check whether the tickets are sold out if (ticket>0){ //Not sold out. Keep selling tickets System.out.println(Thread.currentThread().getName()+"--Selling the third"+ticket+"Ticket"); ticket--; } } }
Run the test code: the test result is the same as the test result of the synchronization code block. There are no duplicate tickets or nonexistent tickets
3. Locking mechanism
java. util. concurrent. locks. The Lock mechanism provides a wider range of locking operations than synchronized code blocks and synchronized methods, and both synchronized code blocks and synchronized methods have the functions of Lock, and Lock can better reflect object-oriented.
Lock lock is also called synchronous lock. Its locking and releasing have been methodized:
public void lock(): Add synchronous lock. public void unlock(: Release the synchronization lock.
Example code:
Two solutions to thread safety:use Lock lock Use steps: 1.Create one at the member location ReentrantLock object 2.Call before code that may have security problems. Lock Methods in interfaces Lock Acquire lock 3.Call after code that may have security problems. Lock Methods in interfaces unLock Release lock //Multithreading is realized by implementing the Runnable interface public class RunnableImp implements Runnable { //Set the total number of votes, 100 tickets private int ticket=100; //1. Create a ReentrantLock object at the member location Lock lock = new ReentrantLock(); //Rewrite the run method, set the thread task, and sell tickets @Override public void run() { //Dead cycle, repeat ticket sales while (true){ //2. call the Lock in the Lock interface before getting code for security problems. lock.lock(); //Check whether the tickets are sold out if (ticket>0){ //Not sold out. Keep selling tickets System.out.println(Thread.currentThread().getName()+"--Selling the third"+ticket+"Ticket"); ticket--; } //3. call the method unLock release lock in the Lock interface after the code that may have security problems. lock.unlock(); } } }
Run the test code: the test result is the same as the test result of the synchronization code block. There are no duplicate tickets or nonexistent tickets
3, Six states of threads
-
New (new status)
- The state when the thread was just created, but the thread has not been started and the start method has not been called.
-
Runnable
- The state in which a thread can run in the java virtual machine. The thread may or may not be running its own code, depending on the processor of the operating system.
- Blocked (lock blocked)
- When a thread attempts to obtain an object lock, but the object lock has been held by other threads, the thread will enter the Blocked blocking state; When the thread holds the lock object, the thread will change to Runnable runnable state.
- Waiting (infinite waiting state)
- When a money process is Waiting for another thread to perform a (wake-up) action, the thread enters the Waiting infinite Waiting state. After entering this state, the thread cannot be awakened automatically. It must wait for another thread to call the notify() or notifyAll() method before it can be called.
- Timed waiting
- Similar to the waiting infinite wait state, when methods with timeout parameters are called, they will enter the Timed Waiting state. This state will remain until the timeout period expires or a wake-up notification is received. A common method with timeout parameters is thread Sleep() and object wait().
- Terminated (dead state)
- The thread that has exited is in this state.
4, Thread waiting and wake-up mechanism
Method used in waiting for wake-up
The wake-up waiting mechanism is used to solve the problem of inter thread communication,
The three methods used are as follows:
- 1.wait() method: when the wait() method is called, the thread will no longer participate in scheduling and enter the wait set, which will not waste CPU resources or compete for locks. This thread state is waiting. At the same time, it will wait for other threads to perform special actions - > notify that the thread waiting on this object will be released from the walt set and re entered into the ready queue
- 2.notify() method: select a thread in the wait set of the notified object and release it. That is, wake up a single thread waiting on this monitor
- 3.notifyAll(): release all threads in the wait set of the notified object. That is, wake up all threads waiting on this monitor
Note: the notified thread cannot enter the execution state immediately because it was interrupted in the synchronization block before. At this time, the thread has no lock object, so it needs to obtain the lock object again. At this time, other threads may compete with it, The thread competes successfully. i will get the lock object before resuming execution before calling to wait() method.
Therefore, it can be concluded that if the thread can obtain the lock, the thread will change from waiting state to running state; Otherwise, the thread just comes out of the wait set and enters the entry set, and the thread will transition from the waiting state to the blocking state again
Details of calling the wait() and notify() methods
- 1. The wait method and notify method must be called by the same lock object. Because the corresponding lock object can wake up the thread after using the wait() method called by the same lock object through notify.
- 2. Both the wait () method and the notify() method are methods of the Object class. Because the lock Object can be any type of Object, and the class to which the Object of any class belongs inherits the Object class. I
- 3.walt() and notlfy() methods must be used in synchronous code blocks or synchronous methods. Because to call these two methods, you must lock the object.
Case: when a customer goes to a beef noodle shop to eat beef noodles, the noodle shop owner is first waiting for the customer to order noodles. When a customer orders a bowl of beef noodles, the boss starts making noodles, and the customer enters the waiting. When the boss's noodles are ready, he will tell the customer that the noodles are ready and the customer starts eating noodles.
Example code:
//Create beef noodle class, class BeefNoodles{ //Types of noodle soup: Golden soup, red soup, String tang; //Taste: not spicy, generally spicy String kouwei; //Face status, with face true and without face false boolean flag =false; } //Create beef noodle restaurant class thread public class BeefNoodlesRestaurant extends Thread { //Create a beef noodle class, beef noodle variable private BeefNoodles beefNoodles; //Assign a value to the beef noodle variable using the parametric construction method public BeefNoodlesRestaurant(BeefNoodles beefnoodles) { this.beefNoodles = beefnoodles; } //Rewrite the run method and set the thread task: making beef noodles @Override public void run() { //Define variables to determine the taste of beef noodles, int count = 2;//The default golden soup is not spicy while (true){//Dead cycle, always making beef noodles //Use synchronized code blocks to protect threads synchronized (beefNoodles){ //Judge the status of beef noodles and decide whether to enter the thread waiting state if (beefNoodles.flag==true){ //Beef noodles already exist (ready), beefNoodles calls the wait() method to enter the wait try{ beefNoodles.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //Wake up the thread and make beef noodles //Set the taste of beef noodles if (count%2==0){ beefNoodles.tang="Golden soup"; beefNoodles.kouwei="Not spicy"; }else if (count%3==0){ beefNoodles.tang="Red soup"; beefNoodles.kouwei="Not spicy"; }else if (count%5==0){ beefNoodles.tang="Golden soup"; beefNoodles.kouwei="Generally spicy"; }else if (count%7==0){ beefNoodles.tang="Red soup"; beefNoodles.kouwei="Generally spicy"; } //Confirm the type of beef noodles System.out.println("Doing"+beefNoodles.tang+beefNoodles.kouwei+"Beef noodles"); //It takes 6 seconds to make beef noodles try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } //The beef noodle shop made beef noodles beefNoodles.flag=true;//Sets the state of the face count++; //Wake up customers to eat noodles beefNoodles.notify(); System.out.println("objective"+beefNoodles.tang+beefNoodles.kouwei+"Beef noodles have been made"); } } } }
//Create client class thread public class KeHu extends Thread { //Create a beef noodle class, beef noodle variable private BeefNoodles beefNoodles; //Assign a value to the beef noodle variable using the parametric construction method public KeHu(BeefNoodles beefnoodles) { this.beefNoodles = beefnoodles; } //Rewrite the run method and set the thread task: eating beef noodles @Override public void run() { int i=1;//Customer serial number while (true){//Dead cycle, always eating beef noodles //Use synchronized code blocks to protect threads synchronized (beefNoodles){ //Judge the status of beef noodles and decide whether to enter the thread waiting state if (beefNoodles.flag==false){ //Beef noodles do not exist (not ready), beefNoodles calls the wait() method to enter the wait try { beefNoodles.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //The state of execution after a thread wakes up System.out.println("customer"+Thread.currentThread().getName()+"-->"+i+"I am eating" +beefNoodles.tang+beefNoodles.kouwei+"Beef noodles"); //The customer finished the beef noodles and modified the status of the noodles beefNoodles.flag =false; //When the noodles are finished, wake up the noodle shop to make noodles beefNoodles.notify(); System.out.println("customer"+Thread.currentThread().getName()+"-->"+i+"It's finished" +beefNoodles.tang+beefNoodles.kouwei+"Beef noodles"+"οΌThe noodle shop continued to make beef noodles"); i++;//Next customer System.out.println("----------------------------------"); } } } }
//Test class public class Test { public static void main(String[] args) { //Create a beef face object BeefNoodles beefNoodles = new BeefNoodles(); //Open the thread of beef noodle restaurant to make noodles new BeefNoodlesRestaurant(beefNoodles).start(); //Start the customer thread and the customer starts eating noodles new KeHu(beefNoodles).start(); } }
Test results: