The failure and reason summary of one-time synchronization method

Keywords: Java

The synchronized keyword can set the synchronization method and synchronization block. The synchronization method will lock the calling object this,
All threads need to acquire this lock before entering. Here I'll take an error example

 1 public class Test {
 2     public static void main(String[] args) {
 3         Account account=new Account(100);
 4         Person p1=new Person(account,80);
 5         Person p2=new Person(account,90);
 6         p1.start();
 7         p2.start();
 8     }    
 9 }
10 
11 class Account{
12     int total;    
13     public Account(int total) {
14         super();
15         this.total=total;        
16     }    
17 }
18 
19 class Person extends Thread{
20     private int reduce;
21     private Account account;
22     public Person(Account account,int reduce) {
23     
24         this.account=account;
25         this.reduce=reduce;
26     }
27     
28     public synchronized void run() {    
29         if (account.total-reduce<0) return ;
30         try {
31             Thread.sleep(200);
32         } catch (InterruptedException e) {
33             // TODO Auto-generated catch block
34             e.printStackTrace();
35         }
36         account.total-=reduce;
37         System.out.println(Thread.currentThread().getName()+"take out"+reduce);
38         System.out.println(Thread.currentThread().getName()+"Surplus"+account.total);
39         
40     }
41 }

 


Thread-0 take out 80
Thread-0 remaining - 70
Thread-1 take out 90
Thread-1 remaining - 70

There is a negative number, which is obviously not locked. Now let's analyze the reasons.
There is one account object, two person objects p1 and p2, p1 and p2 compete for resource account
My initial assumption is that when p1 enters the method, p1 object is locked, and account is also locked as a member of p1. At this time, p2 entry needs to wait until p1 releases the lock, so as to achieve our goal.

The problem is: java specifies that each object has a monitor. Check whether it is locked when using it. Although the person object is locked in the above code, account also has a monitor as an object. Although account is a member of person, the monitor of account is independent and not affected by p1 lock. Now let's go back to the scene restoration. p1 first enters the method, locks p1, and calculates 100-80 = 20. At the same time, p2 enters the method, and checks the account's monitor. If it is not locked, it calculates 20-90 = - 70, which results in an error.

The solution is to lock the account using the synchronization block

 1 public void run() {    
 2         synchronized(account) {
 3         if (account.total-reduce<0) return ;
 4         try {
 5             Thread.sleep(200);
 6         } catch (InterruptedException e) {
 7             // TODO Auto-generated catch block
 8             e.printStackTrace();
 9         }
10         account.total-=reduce;
11         System.out.println(Thread.currentThread().getName()+"take out"+reduce);
12         System.out.println(Thread.currentThread().getName()+"Surplus"+account.total);
13         
14         }
15     }

 


The lesson of this problem is that each object has a separate monitor. To select the right object to lock, based on this synchronization block, we can not only reduce the granularity of lock, but also select the right object to lock.

Posted by anand on Tue, 03 Dec 2019 16:42:13 -0800