Thread synchronization
Concurrency: multiple threads access the same object. Example: tens of thousands of people rob 100 tickets at the same time
When dealing with multithreading, multiple threads access the same object, and some threads also want to modify the object. Thread synchronization is required at this time,
Thread synchronization is actually a waiting mechanism. Multiple threads that need to access this object at the same time enter the waiting pool of this object to form a queue, wait for the previous thread to use it, and then use it for the next thread
Thread synchronization: queue + lock (synchronized)
Problems with thread synchronization (loss of performance):
- Holding a lock by one thread will cause all other threads that need the lock to hang
- In multi-threaded competition, locking and releasing locks will lead to more context switching and scheduling delays, resulting in performance problems
- If a high priority thread waits for a low priority thread to release the lock, it will lead to priority inversion and performance problems
package com.yuanyu.syn; //Unsafe ticket buying public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"Xiao Hong").start(); new Thread(buyTicket,"Xiao Hei").start(); new Thread(buyTicket,"Xiaobai").start(); } } class BuyTicket implements Runnable{ private int ticketNums=10; private boolean flag=true; @Override public void run() { //Buy a ticket while (flag){ buy(); } } private void buy(){ if (ticketNums<=0){ flag=false; return; } //Analog delay try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"Yes"+ticketNums--); } }
Program running results:
Each thread interacts in its own working memory. Improper memory control will cause data inconsistency
package com.yuanyu.syn; //Unsafe withdrawal //Two people go to the bank to withdraw money and open an account public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100,"Small Treasury"); Drawing d1 = new Drawing(account,50,"I"); Drawing d2 = new Drawing(account,100,"You"); d1.start(); d2.start(); } } class Account{ int money; //balance String name; //Card name public Account(int money, String name) { this.money = money; this.name = name; } } //Bank: simulated withdrawal class Drawing extends Thread{ Account account; //account int drawingMoney; //Amount withdrawn int nowMoney; //Money in hand @Override public void run() { if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw"); return; } account.money=account.money-drawingMoney; //Account balance nowMoney=nowMoney+drawingMoney; //Money in hand System.out.println(account.name+"The balance is:"+account.money); System.out.println(this.getName()+"The money in hand is:"+nowMoney); //this.getName()=Thread.currentThread().getName() } public Drawing(Account account, int drawingMoney, String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } }
Program running results:
You don't have enough money to withdraw
Small Treasury balance: 50
The money in my hand is: 50
Thread.sleep() delay can amplify the occurrence of the problem
package com.yuanyu.syn; import java.util.ArrayList; //Thread unsafe collection public class UnsafeTest { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); } ).start(); } //Delay can amplify the occurrence of problems // try { // Thread.sleep(3000); // } catch (InterruptedException e) { //1000 // e.printStackTrace(); // } System.out.println(list.size()); //994 } }
The solution of Concurrency: thread synchronization
Access to "objects" is controlled through the synchronized keyword. Each object corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before it can be executed. Otherwise, the thread will block. Once the method is executed, it will monopolize the lock until the method returns, and the lock will not be released until the method returns. The blocked thread can obtain the lock and continue to execute
[defect]: if a large method is declared synchronized, the efficiency will be affected
Only the contents that need to be modified in the method need to be locked. Too many locks will waste resources
package com.yuanyu.syn; //Buy a ticket safely public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"Xiao Hong").start(); new Thread(buyTicket,"Xiao Hei").start(); new Thread(buyTicket,"Xiaobai").start(); } } class BuyTicket implements Runnable{ private int ticketNums=10; private boolean flag=true; @Override public void run() { //Buy a ticket while (flag){ buy(); } } //The synchronized synchronization method locks this private synchronized void buy(){ if (ticketNums<=0){ flag=false; return; } //Analog delay try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"Yes"+ticketNums--); } }
Program running results:
package com.yuanyu.syn; //Safe withdrawal //Two people go to the bank to withdraw money and open an account public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100,"Small Treasury"); Drawing d1 = new Drawing(account,50,"I"); Drawing d2 = new Drawing(account,100,"You"); d1.start(); d2.start(); } } class Account{ int money; //balance String name; //Card name public Account(int money, String name) { this.money = money; this.name = name; } } //Bank: simulated withdrawal class Drawing extends Thread{ Account account; //account int drawingMoney; //Amount withdrawn int nowMoney; //Money in hand @Override public synchronized void run() { synchronized (account) { if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw"); return; } account.money=account.money-drawingMoney; //Account balance nowMoney=nowMoney+drawingMoney; //Money in hand System.out.println(account.name+"The balance is:"+account.money); System.out.println(this.getName()+"The money in hand is:"+nowMoney); //this.getName()=Thread.currentThread().getName() } } public Drawing(Account account, int drawingMoney, String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } }
The object of the lock must be a variable, and it needs to be added, deleted and modified
The above code example changes not the bank but the account, so synchronized should lock the account
package com.yuanyu.syn; import java.util.ArrayList; //Thread safe collection public class UnsafeTest { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list) { list.add(Thread.currentThread().getName()); } } ).start(); } //Delay can amplify the occurrence of problems try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
10000
synchronized(Obj) {} block (Alt+Ctrl+T)
Obj calls it a synchronization monitor
Obj can be any object, but it is recommended to use shared resources as synchronization monitors
There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself or class
Synchronization monitor execution process
- The first thread accesses, locks the synchronization monitor, and executes the code in it
- The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed
- After the first thread is accessed, lock the synchronization monitor
- The second thread accesses, finds that the synchronization monitor has no lock, and then locks the access