Thread synchronization mechanism
Thread synchronization: multiple threads operate on the same resource
In fact, it is a waiting mechanism. Multiple threads that need to access this object at the same time enter the waiting pool of the object to form a queue
Concurrency: the same object is operated by multiple threads at the same time
For example, grab tickets and withdraw money from the bank
In order to ensure the correctness of data access in the method, the lock mechanism synchronized is added during access. When one thread obtains the exclusive lock of the object and monopolizes resources, other threads must wait and release the lock after use
Using locks can cause the following problems:
- 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
Three unsafe cases
//Ticket grabbing
package Thread.syn; //Unsafe ticket buying public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"yyqx").start(); new Thread(buyTicket,"wyb").start(); new Thread(buyTicket,"ltt").start(); } } class BuyTicket implements Runnable{ private int ticketNums = 10; boolean flag = true; //External stop mode @Override public void run() { //Buy a ticket while (flag) { try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void buy() throws InterruptedException { //Judge whether there are tickets if(ticketNums <= 0){ flag = false; return; } //Analog delay Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"Bought the second"+ticketNums--+"Ticket"); } }
//Bank withdrawal
package Thread.syn; //Unsafe withdrawal public class UnsafeBank { public static void main(String[] args) { //account Account account = new Account(100,"ddd"); Drawing you = new Drawing(account,50,"lllll"); Drawing yourFriend = new Drawing(account,100,"ttttt"); you.start(); yourFriend.start(); } } //account 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;//How much did you withdraw int nowMoney;//How much money do you have now public Drawing(Account account,int drawingMoney,String name){ super(name); this.account = account; this.drawingMoney = drawingMoney; } //Withdraw money @Override public void run(){ //Judge whether there is money if(account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"Insufficient balance, unable to withdraw money"); return; } //sleep can amplify the occurrence of problems try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //Card balance = balance - you get money account.money = account.money - drawingMoney; //The money in your hand nowMoney = nowMoney + drawingMoney; System.out.println(account.name +"The balance is:"+account.money); System.out.println(this.getName()+"Money in hand:"+nowMoney); } }
//Thread unsafe
package Thread.syn; import java.util.ArrayList; import java.util.List; //Thread unsafe collection public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }
Synchronization method and synchronization block
Synchronization method: synchronized method
The synchronized method controls access to the object
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. The blocked thread can obtain the lock and continue to execute
Defect: declaring a large method synchronized will affect efficiency
The content that needs to be modified in the method needs to be locked. Too many locks waste resources
Synchronized blocks: synchronized blocks
synchronized(obj){ }
obj calls it a synchronization monitor
obj = the object of the lock is the amount of change and needs to be added, deleted or modified
Testing a collection of JUC security types
package Thread.syn; import java.util.concurrent.CopyOnWriteArrayList; //Testing a collection of JUC security types public class TestJUC { public static void main(String[] args) throws InterruptedException { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(3000); } }
deadlock
A "deadlock" may occur when a synchronization block has "locks of more than two objects" at the same time
Necessary conditions for deadlock generation:
- Mutex condition: a resource can only be used by one process at a time.
- Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources.
- Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up
- Circular waiting condition: a circular waiting resource relationship is formed between several processes
package Thread; //Deadlock: multiple threads hold each other's required resources, and then form a deadlock public class DeadLock { public static void main(String[] args) { Makeup g1 = new Makeup(0,"li"); Makeup g2 = new Makeup(1,"tang"); g1.start(); g2.start(); } } //Lipstick class Lipstick{ } //mirror class Mirror{ } class Makeup extends Thread{ //Only one resource is needed, which is guaranteed by static static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice;//choice String girlName;//People who use cosmetics Makeup(int choice,String girlName){ this.choice = choice; this.girlName = girlName; } @Override public void run(){ //Make up try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } //Makeup, holding each other's locks, is to get each other's resources private void makeup() throws InterruptedException { if(choice==0){ synchronized (lipstick) {//Get lipstick lock System.out.println(this.girlName+"Get lipstick lock"); Thread.sleep(1000); } synchronized (mirror){//Get the mirror in a second System.out.println(this.girlName+"Get the lock of the mirror"); } }else{ synchronized (mirror) {//Get lipstick lock System.out.println(this.girlName+"Get the lock of the mirror"); Thread.sleep(2000); } synchronized (lipstick){//Get the mirror in a second System.out.println(this.girlName+"Get lipstick lock"); } } } }
Lock lock
juc=java.util.concurrent
ReentrantLock - reentrant lock with the same concurrency and memory semantics as synchronized
class A{
private final ReentrantLock lock = new ReentrantLock();
public void m(){
lock.lock();
try{
//Thread safe code;
}
finally{
lock.unlock();
//If there is an exception in the synchronization code, write unlock() to the finally statement block
}
}
}
Comparison of synchronized and Lock
- Lock is an explicit lock (manual opening and closing lock). synchronized is an implicit lock, which is automatically released out of the scope
- Lock only has code block lock, and synchronized has code block lock and method lock
- Using Lock lock, the JVM takes less time, has better performance and better scalability
- Priority: Lock > synchronize code block (it has entered the method body and allocated corresponding resources) > synchronize method (outside the method body)
package Thread; import java.util.concurrent.locks.ReentrantLock; //Test Lock public class TestLock { public static void main(String[] args) { TestLock2 testLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2).start(); new Thread(testLock2).start(); } } class TestLock2 implements Runnable { int ticketNums = 10; //Define lock lock private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { lock.lock();//Lock try { if (ticketNums > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); } else { break; } }finally{ //Unlock lock.unlock(); } } } }