In depth study of multithreaded 03 synchronized lock 01

Posted by VooDooRT on Fri, 17 Dec 2021 02:22:35 +0100

Pessimistic lock / optimistic lock:
Pessimistic: do not allow other threads to intervene during operation. synchronized,lock
Optimistic: other threads can intervene. Reference version number mechanism, CAS

1. Eight lock phenomenon - lock static state / lock method

1.0 standard access thread, print Email first and then SMS:

class Phone{
    public  synchronized void sendEmail(){
        System.out.println("-----sendEmail");
    }

    public  synchronized void sendSMS(){
        System.out.println("-----sendSMS");
    }

    public void hello(){
        System.out.println("-------hello");
    }
}

public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendEmail();
                },"t1").start();

        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(()->{
            phone.sendSMS();
                },"t2").start();
    }

}

It has nothing to do with sleep() in the middle.

1.1: print email or SMS first

class Phone{
    public synchronized void sendEmail(){
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("-----sendEmail");
    }

    public synchronized void sendSMS(){
        System.out.println("-----sendSMS");
    }
}

public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();



        new Thread(()->{
            phone.sendEmail();
                },"t1").start();


        new Thread(()->{
            phone.sendSMS();
                },"t2").start();
    }

}

result:

-----sendEmail
-----sendSMS

1.2 add normal hello(), print mail or hello()

class Phone{
    public synchronized void sendEmail(){
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("-----sendEmail");
    }

    public synchronized void sendSMS(){
        System.out.println("-----sendSMS");
    }

    public void hello(){
        System.out.println("-------hello");
    }
}

public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        
        new Thread(()->{
            phone.sendEmail();
                },"t1").start();


        new Thread(()->{
            phone.hello();
                },"t2").start();
    }

}

result:

-------hello
-----sendEmail
 The object of the lock is the object. Ordinary lock free methods do not compete. There is no conflict between lock free methods and lock free methods???

1.3 add a phone object, print Email or SMS first?

class Phone{
    public synchronized void sendEmail(){
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("-----sendEmail");
    }

    public synchronized void sendSMS(){
        System.out.println("-----sendSMS");
    }

    public void hello(){
        System.out.println("-------hello");
    }
}

public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone1 = new Phone();

        new Thread(()->{
            phone.sendEmail();
                },"t1").start();


        new Thread(()->{
            phone1.sendSMS();
                },"t2").start();
    }

}

result:

-----sendSMS
-----sendEmail

1.4 for two static synchronization methods, the same mobile phone, print E or S first?

The common synchronization method locks the instance object of new; Static methods lock classes..

class Phone{
    public static synchronized void sendEmail(){
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("-----sendEmail");
    }

    public static synchronized void sendSMS(){
        System.out.println("-----sendSMS");
    }

}

public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendEmail();
                },"t1").start();


        new Thread(()->{
            phone.sendSMS();
                },"t2").start();
    }

}

result:

-----sendEmail
-----sendSMS

1.5 two phone objects, two static synchronization methods, print email or SMS first?

-----sendEmail
-----sendSMS

1.6: a static synchronization method, a common synchronization method, e-mail or SMS?

class Phone{
    public static synchronized void sendEmail(){
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("-----sendEmail");
    }

    public  synchronized void sendSMS(){
        System.out.println("-----sendSMS");
    }

}

public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendEmail();
                },"t1").start();


        new Thread(()->{
            phone.sendSMS();
                },"t2").start();
    }

}

result:

-----sendSMS
-----sendEmail
 Conclusion: there are two different locks: class lock and object lock accessed by two threads

1.7: a static synchronization method, a common synchronization method, and two phone objects:

class Phone{
    public static synchronized void sendEmail(){
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("-----sendEmail");
    }

    public  synchronized void sendSMS(){
        System.out.println("-----sendSMS");
    }

}

public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone1 = new Phone();

        new Thread(()->{
            phone.sendEmail();
                },"t1").start();


        new Thread(()->{
            phone1.sendSMS();
                },"t2").start();
    }

}

result:

-----sendSMS
-----sendEmail

Programming protocol:

In addition, the loss of lock performance shall be considered, and no lock shall be available as far as possible; If the block can be locked, the method body will not be locked; If you can use object locks, you don't need class locks.

★ conclusion:

0-1. If there are multiple synchronized methods in an object, as long as one thread calls one of the synchronized methods at a certain time, other threads can only wait.
The lock is the current object this. After being locked, other objects cannot enter the synchronized method of the current thread.

2-3. After adding the common method, it has nothing to do with the synchronous lock;
After adding an object, there is no competition for resources

4-5. After changing to static methods, the situation changes again: (class lock or object lock?)

  • Common synchronization method: the current instance object of the lock, usually tihs, is a specific mobile phone. All common synchronization methods use the same instance object itself
  • For static synchronization method: lock the current class object, such as phone Class is the only class template;
  • For fast synchronization methods, the lock is the object in synchronized parentheses

6-7: common synchronization method: instance object lock is used
Static synchronization method: class lock is used
Object lock and class lock are two different locks!!! Irrelevant!!!

2.synchronized bytecode

2.1 lock code block normally

public class Lock8ByteCode {
    Object object=  new Object();

    public void m1(){

        synchronized (object){
          System.out.println("-------im synchronized");
       }

    }

    public static void main(String[] args) {

    }
}

Bytecode:

		 6: monitorenter
         7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         10: ldc           #5                  // String -------im synchronized
         12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         15: aload_1
         16: monitorexit
         17: goto          25
         20: astore_2
         21: aload_1
         22: monitorexit

One moniterenter corresponds to two monitorexit s:
One exit exits the code block normally, and the other guarantees to exit the code block when an exception occurs

Add a runtime exception to the locked code block:

public class Lock8ByteCode {
    Object object=  new Object();

    public void m1(){

        synchronized (object){
          System.out.println("-------im synchronized");
          throw new RuntimeException("-----exit");
       }

    }

    public static void main(String[] args) {

    }
}

Bytecode:

   6: monitorenter
       7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: ldc           #5                  // String -------im synchronized
      12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      15: new           #7                  // class java/lang/RuntimeException
      18: dup
      19: ldc           #8                  // String -----exit
      21: invokespecial #9                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
      24: athrow
      25: astore_2
      26: aload_1
      27: monitorexit
      28: aload_2
      29: athrow

You can see that the monitorexit is wrapped by two athrow s, and there is only one monitorexit

2.2 add to the method:

Calling the instruction will check the ACC of the method_ Whether the synchronized access flag is set. If it is set, the execution thread will hold the monitor first, then execute the method, and release it after completion (whether normal completion or abnormal completion).

2.3 synchronized static synchronization method:

One more ACC_STATIC, others are the same as 2.2.

Why can any object become a lock

ObjectMonitor.java→ObjectMonitor.cpp→objectMonitor.hpp
Each object is born with an object monitor.

Topics: Java Multithreading