Thread synchronization mechanism
Thread synchronization refers to multiple threads operating on the same resource
Concurrency means that the same object is operated by multiple threads at the same time
Three unsafe cases
Case 1: ticket grabbing
//Unsafe ticket buying public class UnsafeBuyTicket { public static void main(String args[]){ BuyTicket station = new BuyTicket(); new Thread(station,"student").start(); new Thread(station,"teacher").start(); new Thread(station,"cattle").start(); } } class BuyTicket implements Runnable{ //ticket private int ticketNums = 10; boolean flag = true; //External stop mode 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(100); //Buy a ticket System.out.println(Thread.currentThread().getName() + "Get" + ticketNums--); } }
You can also get - 1 because:
Each thread interacts in its own working memory. Improper memory control will lead to inconsistent data
The popular explanation is that many people may grab one ticket at the same time, but there is only one ticket, so there will be 0 and - 1
Case 2: bank withdrawal
//Unsafe withdrawal //Two people go to the bank to withdraw money and open an account public class UnsafeBank { public static void main(String agrs[]){ //account Account account = new Account(100,"Marriage fund"); Drawing you = new Drawing(account,50,"you"); Drawing girlFriend = new Drawing(account,100,"girlFriend"); you.start(); girlFriend.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 //How much did you withdraw int drawingMoney; //How much money do you have now int nowMoney; public Drawing(Account account,int drawingMoney,String name){ super(name); //It is equivalent to the parent class constructor (name). Drawing inherits Thread. One constructor in Thread class is String name this.account = account; this.drawingMoney = drawingMoney; } //Withdraw money public void run(){ //Judge whether there is money if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw"); return; } //Simulate a delay (not necessarily, here is just to find out the error) //sleep can amplify the occurrence of problems //Because the real code is definitely not used by you alone, but by millions of people 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); //Thread.currentThread().getName() = this.getName() System.out.println(this.getName()+"Money in hand:"+nowMoney); } }
Case 3: thread
import java.util.ArrayList; import java.util.List; public class UnsafeList { public static void main(String agrs[]){ List<String> list = new ArrayList<String>(); for (int i=0;i<10000;i++){ new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } try{ Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(list.size()); } }
I can't get to 10000 because I have to go together...
Synchronization method and synchronization block
Synchronization method
2. 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 execution, 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 blocked thread can obtain the lock and continue to execute
Defect: declaring a large method synchronized will affect efficiency
Disadvantages of synchronization method
The content that needs to be modified in the method needs to be locked. Too many locks waste resources
Practice:
Modification of three unsafe cases:
Case 1:
//Unsafe ticket buying public class UnsafeBuyTicket { public static void main(String args[]){ BuyTicket station = new BuyTicket(); new Thread(station,"student").start(); new Thread(station,"teacher").start(); new Thread(station,"cattle").start(); } } class BuyTicket implements Runnable { //ticket private int ticketNums = 10; boolean flag = true; //External stop mode public void run() { //Buy a ticket while (flag) { try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //The synchronized synchronization method locks this private synchronized void buy() throws InterruptedException { //Judge whether there are tickets if (ticketNums <= 0) { flag = false; return; } //Analog delay Thread.sleep(100); //Buy a ticket System.out.println(Thread.currentThread().getName() + "Get" + ticketNums--); } }
This gives everyone else a chance to unlock the lock first, because it gives everyone else a big chance to unlock the lock first.
Modification:
//Unsafe ticket buying public class UnsafeBuyTicket { public static void main(String args[]){ BuyTicket station = new BuyTicket(); new Thread(station,"student").start(); new Thread(station,"teacher").start(); new Thread(station,"cattle").start(); } } class BuyTicket implements Runnable { //ticket private int ticketNums = 10; boolean flag = true; //External stop mode public void run() { //Buy a ticket while (flag) { try { //Simulate the delay to avoid one person getting all the tickets. The sleep should be written here because the sleep will not unlock Thread.sleep(100); buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //The synchronized synchronization method locks this private synchronized void buy() throws InterruptedException { //Judge whether there are tickets if (ticketNums <= 0) { flag = false; return; } //Buy a ticket System.out.println(Thread.currentThread().getName() + "Get" + ticketNums--); } }
Because the buy method is locked, only one thread can call it at a time
Case 2:
If the method of case 1 is also used for modification:
//Unsafe withdrawal //Two people go to the bank to withdraw money and open an account public class UnsafeBank { public static void main(String agrs[]){ //account Account account = new Account(100,"Marriage fund"); Drawing you = new Drawing(account,50,"you"); Drawing girlFriend = new Drawing(account,100,"girlFriend"); you.start(); girlFriend.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 //How much did you withdraw int drawingMoney; //How much money do you have now int nowMoney; public Drawing(Account account,int drawingMoney,String name){ super(name); //It is equivalent to the parent class constructor (name). Drawing inherits Thread. One constructor in Thread class is String name this.account = account; this.drawingMoney = drawingMoney; } //Withdraw money public synchronized void run(){ //Modification method of case 1 //Judge whether there is money if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw"); return; } //Simulate a delay (not necessarily, here is just to find out the error) //sleep can amplify the occurrence of problems //Because the real code is definitely not used by you alone, but by millions of people 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); //Thread.currentThread().getName() = this.getName() System.out.println(this.getName()+"Money in hand:"+nowMoney); } }
The result is still wrong:
This is because the default lock of synchronized is this, and the bank to be locked here.
That is, locking the bank instead of locking this refers to the current class, that is, the class drawing of the bank class
The synchronized method wants the lock object to be the class of the current class. Either there can only be one instance object of the bank, or the synchronized method is decorated with static. This can also ensure that the lock object is the class of the class
And he has two bank instance objects, you and girlfriend. Then these two instance objects open two threads. The lock object used by each thread is the current instance object. If the lock object is different, synchronization cannot be realized.
The simplest way is to use the synchronized code block to put the class of the current class in the synchronized() {} and parentheses. Because a class can only have one class, it can ensure that the lock objects are the same and realize synchronization
ps: as long as there is a unique object class in the parentheses, you can actually remember the following three points, and you won't make any mistake about the synchronization method or the lock object in the synchronization code block at any time
For normal synchronization methods, the lock is the current instance object. If there are multiple instances, the lock objects must be different and synchronization cannot be achieved.
For static synchronization methods, the lock is the Class object of the current Class. There are multiple instances, but the lock object is the same, and synchronization can be completed.
For synchronized method blocks, locks are objects configured in synchronized parentheses. It is better to have only one object. For example, if the class of the current class has only one lock object, synchronization can also be realized.
Correct writing:
Synchronization block
1. Synchronization block: synchronized (Obj) {}
2. Obj calls it synchronization monitor
(1) Obj can be any object, but it is recommended to use shared resources as synchronization monitors
(2) There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this
//Unsafe withdrawal //Two people go to the bank to withdraw money and open an account public class UnsafeBank { public static void main(String agrs[]){ //account Account account = new Account(100,"Marriage fund"); Drawing you = new Drawing(account,50,"you"); Drawing girlFriend = new Drawing(account,100,"girlFriend"); you.start(); girlFriend.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 //How much did you withdraw int drawingMoney; //How much money do you have now int nowMoney; public Drawing(Account account,int drawingMoney,String name){ super(name); //It is equivalent to the parent class constructor (name). Drawing inherits Thread. One constructor in Thread class is String name this.account = account; this.drawingMoney = drawingMoney; } //Withdraw money public void run(){ //The object of the lock is the amount of change, which needs to be added, deleted and modified synchronized (account){ //After modification //Judge whether there is money if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw"); return; } //Simulate a delay (not necessarily, here is just to find out the error) //sleep can amplify the occurrence of problems //Because the real code is definitely not used by you alone, but by millions of people 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); //Thread.currentThread().getName() = this.getName() System.out.println(this.getName()+"Money in hand:"+nowMoney); } } }
Case 3:
import java.util.ArrayList; import java.util.List; public class UnsafeList { public static void main(String agrs[]){ List<String> list = new ArrayList<String>(); for (int i=0;i<10000;i++){ new Thread(()->{ synchronized (list) { list.add(Thread.currentThread().getName()); } }).start(); } try{ Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(list.size()); } }