1: Eight lock types

1.1: Standard access to two threads AB, do I print T1 or T2 first?

public class SyncUnit { public synchronized void t1() { System.out.println("t1"); } public synchronized void t2() { System.out.println("t2"); } public static void main(String[] args) throws Exception{ SyncUnit syncUnit = new SyncUnit(); new Thread(() -> { syncUnit.t1(); }).start(); Thread.sleep(100); new Thread(() -> { syncUnit.t2(); }).start(); }}Copy the code

1.2: pause the T1 method for 3 seconds, do you want to print T1 or T2 first?

public class SyncUnit {

    public synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

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

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();

        Thread.sleep(100);

        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }
Copy the code

1.3: Add a normal hello() method. Do you want to print t1 or hello???? first

public class SyncUnit { public synchronized void t1(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1"); } public synchronized void t2() { System.out.println("t2"); } public void hello() { System.out.println("hello"); } public static void main(String[] args) throws Exception{ SyncUnit syncUnit = new SyncUnit(); new Thread(() -> { syncUnit.t1(); }).start(); Thread.sleep(100); new Thread(() -> { syncUnit.hello(); }).start(); }}Copy the code

1.4: There are two SyncUnit objects now. Do I print T1 or T2 first?

public class SyncUnit {

    public synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

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

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        SyncUnit syncUnit1 = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit1.t2();
        }).start();
    }
Copy the code

1.5: Two static synchronization methods, one SuncUnit object, do you print T1 or T2???? first

public class SyncUnit { public static synchronized void t1(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1"); } public static synchronized void t2() { System.out.println("t2"); } public static void main(String[] args) throws Exception{ SyncUnit syncUnit = new SyncUnit(); new Thread(() -> { syncUnit.t1(); }).start(); Thread.sleep(100); new Thread(() -> { syncUnit.t2(); }).start(); }}Copy the code

1.6: Two static synchronization methods, two SyncUnit objects, whether to print T1 or T2 first

public class SyncUnit { public static synchronized void t1(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1"); } public static synchronized void t2() { System.out.println("t2"); } public static void main(String[] args) throws Exception{ SyncUnit syncUnit = new SyncUnit(); SyncUnit syncUnit1 = new SyncUnit(); new Thread(() -> { syncUnit.t1(); }).start(); Thread.sleep(100); new Thread(() -> { syncUnit1.t2(); }).start(); }}Copy the code

1.7: a static synchronization method, a normal synchronization method, a SyncUnit object, whether to print T1 or T2 first

public class SyncUnit { public static synchronized void t1(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1"); } public synchronized void t2() { System.out.println("t2"); } public static void main(String[] args) throws Exception{ SyncUnit syncUnit = new SyncUnit(); new Thread(() -> { syncUnit.t1(); }).start(); Thread.sleep(100); new Thread(() -> { syncUnit.t2(); }).start(); }}Copy the code

1.8 One static synchronization method, common synchronization method, two SyncUnit objects, whether to print T1 or T2 first

public class SyncUnit { public static synchronized void t1(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1"); } public synchronized void t2() { System.out.println("t2"); } public static void main(String[] args) throws Exception{ SyncUnit syncUnit = new SyncUnit(); SyncUnit syncUnit1 = new SyncUnit(); new Thread(() -> { syncUnit.t1(); }).start(); Thread.sleep(100); new Thread(() -> { syncUnit1.t2(); }).start(); }}Copy the code

Conclusion: 1.9

  • 1: sync modifies the normal method, then the current object of the lock, the same instance can have multiple objects, one object is a lock
  • 2: sync modifies static methods so that the current instance is locked and there is only one instance in the current context
  • 3: In view of the last several cases, I hope you can think carefully. If you have any questions, please contact me

Sync is the underlying implementation principle

2.1: Synchronized code blocks

public class SyncUnit { private Object obj = new Object(); public void t1() { synchronized (obj) { System.out.println("123"); }}}Copy the code

Use javap -c syncUnit. class to view the bytecode, and the underlying monitorenter and Monitorexit are used

2.2: amonitorenterThere must be more than onemonitorexit??

public class SyncUnit { private Object obj = new Object(); public void t1() { synchronized (obj) { System.out.println("123"); throw new RuntimeException("123"); }}}Copy the code

Looking at the bytecode file, you’ll see only one Monitorexit

2.3 Synchronization Methods

public class SyncUnit { private Object obj = new Object(); public synchronized void t1() { System.out.println("123"); }}Copy the code

Looking at the bytecode, the synchronization method is decorated with ACC_SYNCHRONIZED, as is the synchronization method

4: Sync lock upgrade

4.1: no lock

  • Lockless means that there is no contention between threads at all, and methods do not need to be added to do any synchronization

4.2: biased locking

  • The author of HotSpot found through research that in most cases, locks are not only not contested by multiple threads, but are always acquired by the same thread multiple times, and biased locks are introduced to make the cost of lock acquisition lower. Biased locking is what happens in this situation, and it only happens to improve performance when synchronization is performed by one thread

4.3: Lightweight locks

  • When there is a lock contention, lightweight locks are acquired by means of spinlocks, that is, CAS

5: lock elimination and lock coarsening

5.1: Lock elimination: Each time t1 method is executed, an Object Object will be created. As we know, an Object is a lock, so there are n threads executing T1 method at the same time, n objects will be created. That is, there are n lockssynchronizedThere’s no difference at all. Here it isLock elimination

public class SyncUnit { public void t1() { Object obj = new Object(); synchronized (obj){ System.out.println("123"); }}}Copy the code

5.2: Lock coarsening, although 4 are added heresynchornizedBut you could have done it with just one, and all of these successive operationsRepeatedly lock and unlock the same object. Even without thread contention, frequent mutex synchronization can lead to unnecessary performance losses

public class SyncUnit { private Object obj = new Object(); public void t1() { synchronized (obj){ System.out.println("1"); } synchronized (obj){ System.out.println("2"); } synchronized (obj){ System.out.println("3"); } synchronized (obj){ System.out.println("4"); Public void t1() {synchronized (obj){system.out.println ("1"); System.out.println("2"); System.out.println("3"); System.out.println("4"); }}Copy the code