Synchronized keyword has always been an elder role in multi-threaded concurrent programming, which is a barrier to learn concurrent programming and the only way to advanced Java development.

1. Synchronized nature

Synchronized is a built-in locking mechanism provided by Java and has the following two features:

  • Mutual exclusion: this lock can be held by at most one thread at a time. When thread 1 attempts to acquire a lock held by thread 2, thread 1 must wait or block until thread 2 releases the lock. If thread 2 never releases the lock, thread 1 will wait forever.

  • Reentrancy: A thread can acquire a lock that it already owns.

B. synchronized C. synchronized D. synchronized

Every object in Java can be used as a lock. The usage of synchronized can be divided into the following two types according to the different objects of the lock:

  • Object locks: includes method locks (default lock object is this current instance object) and synchronized code block locks (specify your own lock object)

  • Class lock: Refers to synchronized that modifies static methods or specifies locks as Class objects.

Seven cases of multi-thread access to synchronous methods

This part is aimed at the interview often test 7 cases of code actual combat and principle explanation.

1. A synchronous method in which two threads simultaneously access an object

/** * public class Demo1 implements Runnable {static Demo1 instance = new Demo1(); @Override public voidrun() {
        fun();
    }

    public synchronized void fun() {
        System.out.println(Thread.currentThread().getName() + "Up and running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "End of run");
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()) {

        }
        System.out.println("finished"); }}Copy the code

Result: Two threads execute sequentially.

Thread1 and thread2 share the same lock instance. Only one thread can acquire the lock at a time; Thread1 starts first, obtains the lock first, and runs first, while thread2 waits. When thread1 releases the lock, Thread2 acquires the lock and executes.

2. Two threads access synchronized methods of two objects

public class Demo2 implements Runnable{

    static Demo2 instance1 = new Demo2();
    static Demo2 instance2 = new Demo2();

    @Override
    public void run() {
        fun();
    }

    public synchronized void fun() {
        System.out.println(Thread.currentThread().getName() + "Up and running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "End of run");
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()) {

        }
        System.out.println("finished"); }}Copy the code

Result: Two threads execute in parallel.

Explanation: thread1 use lock object is instance1, use the lock object is instance2 thread2, two objects using the lock object is not the same, so the mutual influence between threads, are executed in parallel.

3. Both threads access synchronized static methods

public class Demo3 implements Runnable{

    static Demo3 instance1 = new Demo3();
    static Demo3 instance2 = new Demo3();

    @Override
    public void run() {
        fun();
    }

    public static synchronized void fun() {
        System.out.println(Thread.currentThread().getName() + "Up and running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "End of run");
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()) {

        }
        System.out.println("finished"); }}Copy the code

Result: Two threads execute sequentially.

Explanation: Although two threads use two different instance instances, but as long as the method is static, the corresponding lock object is the same lock, need to obtain the lock successively to execute.

4. Access both synchronous and asynchronous methods

public class Demo4 implements Runnable {

    static Demo4 instance = new Demo4();

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")){
            fun1();
        }else{
            fun2();
        }
    }

    public synchronized void fun1() {
        System.out.println(Thread.currentThread().getName() + "Up and running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "Fun1 run completed");
    }

    public void fun2() {
        System.out.println(Thread.currentThread().getName() + "Fun2 is running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "End of run");
    }
    public static void main(String[] args) {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()) {

        }
        System.out.println("finished"); }}Copy the code

Result: Two threads execute in parallel.

Note: The synchronize keyword works only for fun1 and does not affect other methods. That is, synchronized methods do not affect asynchronous methods, and both methods execute in parallel.

5. Different common synchronization methods for accessing the same object

public class Demo5 implements Runnable {

    static Demo5 instance = new Demo5();

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")){
            fun1();
        }else{
            fun2();
        }
    }

    public synchronized void fun1() {
        System.out.println(Thread.currentThread().getName() + "Up and running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "Fun1 run completed");
    }

    public synchronized void fun2() {
        System.out.println(Thread.currentThread().getName() + "Fun2 is running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "End of run");
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()) {

        }
        System.out.println("finished"); }}Copy the code

Result: Sequential execution.

Explanation: Both methods share the instance lock, so they cannot be run at the same time.

6. Access both static synchronized and non-static synchronized methods

public class Demo6 implements Runnable{

    static Demo6 instance = new Demo6();

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")){
            fun1();
        }else{
            fun2();
        }
    }

    public static synchronized void fun1() {
        System.out.println(Thread.currentThread().getName() + "Up and running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "Fun1 run completed");
    }

    public synchronized void fun2() {
        System.out.println(Thread.currentThread().getName() + "Fun2 is running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "End of run");
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()) {

        }
        System.out.println("finished"); }}Copy the code

Result: Two threads execute in parallel

The static keyword locks the class itself. There is no static keyword; the object instance is locked; The lock is not the same lock, there is no conflict between the two locks; So two threads can execute in parallel.

7. The lock is released after the method throws an exception

public class Demo7 implements Runnable{

    static Demo7 instance = new Demo7();

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")){
            fun1();
        }else{
            fun2();
        }
    }

    public synchronized void fun1() {
        System.out.println(Thread.currentThread().getName() + "Up and running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        throw new RuntimeException();
        //System.out.println(Thread.currentThread().getName() + "Fun1 run completed");
    }

    public synchronized void fun2() {
        System.out.println(Thread.currentThread().getName() + "Fun2 is running");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "End of run");
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()) {

        }
        System.out.println("finished"); }}Copy the code

Result: Thread1 encounters an exception and does not finish running. Thread2 starts and runs until it finishes.

Explanation: The JVM automatically releases the lock after the method throws an exception.

8. Summary of the seven situations mentioned above

Three key ideas:

  1. A lock can only be acquired by one thread at a time, and any thread that does not acquire the lock must wait.

  2. Each instance should have its own lock, different instances do not affect each other; Exception: all objects share the same lock when the lock object is. Class and when synchronized modifiers a static method.

  3. The lock is released either when the method completes properly or when the method throws an exception.

4. Comparison between Synchronized and ReentrantLock

Although ReentrantLock is a more advanced locking mechanism, synchronized still has the following advantages:

  1. Synchronized is familiar to more developers as a built-in lock with simple code;

  2. Synchronized is more secure than ReentrantLock. Forgetting to release a lock in finally can cause code to appear to work well, but in fact it can cause problems.

  3. Synchronized gives a sense of which locks were acquired in which call frames in a thread dump, and can detect and identify deadlocked threads.

Five, the summary

  1. The synchronized keyword is a mutually exclusive and reentrant built-in locking mechanism provided by Java.

  2. It can be used in two ways: object locking and class locking.

  3. Although synchronized has disadvantages of less flexibility and lower efficiency compared with advanced locks, it also has its own advantages: security is still an important knowledge point that has to be learned in the field of concurrent programming.