1 introduction

This article mainly through a simple demo to explain the use of synchronized lock and the use of synchronized lock related matters to note down and convenient memory.

Synchronized locks are built into the JVM and are different from ReentrantLock locks. The synchronized keyword can modify methods as well as code blocks. Synchronized keyword can be used to modify static and non-static methods. Similarly, the synchronized keyword modifies code blocks by modifying objects as well as classes. Of course, synchronized has a different scope for modifying static methods/classes than non-static methods/objects. Below through a variety of demo to explain the use of synchronized and precautions.

2 synchronized lock

The scope of a synchronized class lock is class-level and cannot be invalidated by different objects executing the same class.

2.1 synchronized Specifies that two static methods of the same class are mutually exclusive

public class SynchronizeAndClassLock {
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            // New a ClassLock object
            new ClassLock().test1();
        }).start();

        new Thread(() -> {
            // New another ClassLock object
            newClassLock().test2(); }).start(); }}class ClassLock {
    public synchronized static void test1(a){
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {}
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " end...");
    }
	Public static void test2(){public static void test2(){
    public synchronized static void test2(a){
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {}
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}Copy the code

Running results:

【 Conclusion 】 When two threads simultaneously execute two different synchronized static methods on different objects generated by the same class, the class lock takes effect, although the objects are different, because the two threads use the same class lock. On the other hand, if test2 is not synchronized, and only test1 is synchronized, the two methods will not be mutually exclusive either.

2.2 Synchronized A static method of the same class and the current class are mutually exclusive

public class SynchronizeAndClassLock2 {
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            // New a ClassLock2 object
            new ClassLock2().test1();
            // ClassLock2.test1();
        }).start();

        new Thread(() -> {
            // New another ClassLock2 object
            new ClassLock2().test2();
            // ClassLock2.test2();}).start(); }}class ClassLock2 {
    public synchronized static void test1(a){
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {}
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " end...");
    }

    public static void test2(a){
    	/ / 【 note 】 synchronized (SynchronizeAndClassLock2. Class) do not mutually exclusive
        synchronized (ClassLock2.class) {
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {}
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}}Copy the code

Running results:

【 Conclusion 】 When two threads execute a synchronized static method and a synchronized static method with a block of synchnized code, the lock takes effect, although it is a different object because both threads use the same class lock. On the other hand, if you’re modifying different classes, because class locks are different, they’re definitely not mutually exclusive, Such as the method of test2 synchronized (ClassLock2. Class) this code into a synchronized (SynchronizeAndClassLock2. Class), not mutually exclusive.

2.3 synchronized Is mutually exclusive when it modifies the same static object

public class SynchronizeAndClassLock10 {

    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            new RunObject1().test1();
        }).start();

        new Thread(() -> {
            newRunObject2().test2(); }).start(); }}class RunObject1 {
    public static void test1(a){
    	// [1] synchronized (StaticLock2. StaticLock1) {
        synchronized (StaticLock2.staticLock) {
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {}
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}}class RunObject2 {
    public static void test2(a) {
    	// [2] synchronized (StaticLock2. StaticLock2) {
        synchronized (StaticLock2.staticLock) {
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {}
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}}class StaticLock2 {
    public static Object staticLock = new Object();
}
Copy the code

Running results:

[Conclusion] Synchronized is mutually exclusive when it modifies static objects of the same class. Conversely, if it modifies different static objects, it will definitely not be mutually exclusive. For example, the synchronized code marked [1] and [2] above is used in combination.

3 Synchronized Object lock

The scope of synchronized object lock is object-level, that is, it only applies to the same object. If two different objects of the same class are not mutually exclusive, that is, there is no effect.

3.1 Synchronized Specifies that two non-static methods of the same class object are mutually exclusive

public class SynchronizeAndObjectLock2 {
    public static void main(String[] args) throws Exception {
        / / 【 note 】 if and only if the same SynchronizeAndObjectLock2 object
        SynchronizeAndObjectLock2 synchronizeAndObjectLock2 = new SynchronizeAndObjectLock2();
        new Thread(() -> {
            synchronizeAndObjectLock2.test1();
        }).start();

        new Thread(() -> {
            synchronizeAndObjectLock2.test2();
        }).start();
    }
    public synchronized void test1(a){
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {}
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " end...");
    }

    public synchronized void test2(a){
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {}
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}Copy the code

Running results:

【 Conclusion 】 When two threads simultaneously execute different (identical) methods on the same object modified by synchronized, the lock takes effect because both threads use the same object lock

3.2 Synchronized Non-static methods that modify objects of the same class and the current object are mutually exclusive

public class SynchronizeAndObjectLock3 {
    public static void main(String[] args) throws Exception {
        / / 【 note 】 if and only if the same SynchronizeAndObjectLock3 object
        SynchronizeAndObjectLock3 synchronizeAndObjectLock3 = new SynchronizeAndObjectLock3();
        new Thread(() -> {
            synchronizeAndObjectLock3.test1();
        }).start();

        new Thread(() -> {
            synchronizeAndObjectLock3.test2();
        }).start();
    }
    public void test1(a){
        synchronized(this) {
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {}
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}public synchronized void test2(a){
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {}
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}Copy the code

Running results:

【 Conclusion 】 The snchronized modified non-static method is mutually exclusive with synchronized(this), so it can be seen that the snchronized modified non-static method essentially locks the current object.

3.3 synchronized Modifies two non-static methods of different objects that are not mutually exclusive

public class SynchronizeAndObjectLock {
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            // A SynchronizeAndObjectLock object is new
            new SynchronizeAndObjectLock().test1();
        }).start();

        new Thread(() -> {
            // Another SynchronizeAndObjectLock object is new
            new SynchronizeAndObjectLock().test2();
        }).start();
    }
    public synchronized void test1(a){
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {}
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " end...");
    }

    public synchronized void test2(a){
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {}
        System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}Copy the code

Running results:

【 Conclusion 】 Two threads simultaneously execute different (same) methods on different objects modified by synchronized, and the lock does not take effect because the two threads use different object locks.

3.4 Synchronized code blocks that modify the same object are mutually exclusive

public class SynchronizeAndObjectLock5 {
    private Object objectLock = new Object();

    public static void main(String[] args) throws Exception {
        
        SynchronizeAndObjectLock5 synchronizeAndObjectLock5 = new SynchronizeAndObjectLock5();
        new Thread(() -> {
            synchronizeAndObjectLock5.test1();
        }).start();

        new Thread(() -> {
            synchronizeAndObjectLock5.test2();
        }).start();
    }
    public void test1(a){
        synchronized(objectLock) {
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {}
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}public void test2(a){
        synchronized(objectLock) {
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {}
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}}Copy the code

Running results:

【 Conclusion 】 Synchronized code blocks are mutually exclusive when they modify the same object. If synchronized code blocks modify different objects, they are not mutually exclusive.

4 synchronized modifies current classes and current objects without being mutually exclusive

public class ClassAndObjectLock {
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            ClassAndObjectLock.test1();
        }).start();

        new Thread(() -> {
            new ClassAndObjectLock().test2();
        }).start();
    }
    public static void test1(a){
        synchronized (ClassAndObjectLock.class) {
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {}
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}public void test2(a){
        synchronized (this) {
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " begin...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {}
            System.out.println(new Date() + "" + Thread.currentThread().getName() + " end..."); }}}Copy the code

Running results:

【 Conclusion 】 Class locking and object locking are independent of each other.

5 Precautions for synchronized locks

5.1 Synchronized locks cannot be interrupted

To simulate the uninterruptibility of synchronized locks, let two threads enter a deadlock, and then interrupt one of them with the main thread to see if the interrupted thread can release the lock and wake up.

public class DeadLockCannotInterruptDemo {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) throws Exception {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run(a) {
                synchronized (lock1) {
                    System.out.println(Thread.currentThread().getName() + " get lock1");
                    try {
                        Thread.sleep(10);
                        synchronized (lock2) {
                            System.out.println(Thread.currentThread().getName() + " get lock2"); }}catch(InterruptedException e) { e.printStackTrace(); }}}}); Thread threadB =new Thread(new Runnable() {
            @Override
            public void run(a) {
                synchronized (lock2) {
                    System.out.println(Thread.currentThread().getName() + " get lock2");
                    try {
                        Thread.sleep(10);
                        synchronized (lock1) {
                            System.out.println(Thread.currentThread().getName() + " get lock1"); }}catch(InterruptedException e) { e.printStackTrace(); }}}}); threadA.start(); threadB.start(); TimeUnit.SECONDS.sleep(3);
        System.out.println("main thread begin to interrupt " + threadA.getName() + " and " + threadA.getName() + " will release lock1..."); threadA.interrupt(); }}Copy the code

Running results:

【 Conclusion 】 As shown in the figure above, Thread-0 does not release the lock and wake up after the main Thread interrupts. Similarly, ReentrantLock’s tryLock or lockInterruptibly can be interrupted.

5.2 Synchronized locks can be reentrant

5.2.1 Different methods synchronized is reentrant

public class SynchronizeAndReentrant {
    public static void main(String[] args) throws Exception {
        SynchronizeAndReentrant synchronizeAndReentrant = new SynchronizeAndReentrant();
        synchronizeAndReentrant.test1();
    }
    public synchronized void test1(a){
        System.out.println(" test1 method is called...");
        test2();
    }

    public synchronized void test2(a){
        System.out.println(" test2 method is called..."); }}Copy the code

Running results:

5.2.2 Synchronized is reentrant in the same way

public class SynchronizeAndReentrant2 {
    int i = 1;
    public static void main(String[] args) throws Exception {
        SynchronizeAndReentrant2 synchronizeAndReentrant = new SynchronizeAndReentrant2();
        synchronizeAndReentrant.test1();
    }
    public synchronized void test1(a){

        System.out.println(" test1 method is called " + i++ + "st time..." );
        while(i < 5) { test1(); }}}Copy the code

Running results:

5.3 Synchronized Locks Do not support the timeout function

Synchronized locks do not have a timeout function, while ReentrantLock’s tryLock has a timeout function. If a lock is not obtained within a specified time, the thread will wake up, preventing deadlocks.

5.4 Wake up/Wait Requires a synchronized lock

public class NotifyNeedSynchronized {
    public static Object lock = new Object();
    public static void main(String[] args) throws Exception{
        / / throw IllegalMonitorStateException
        //lock.notify();lock.wait(); }}Copy the code

Running results:

【 conclusion 】 use Object notify and wait, must use synchronized lock, otherwise you will be thrown IllegalMonitorStateException.

5.5 Narrow the range of synchronized locks to ensure performance

When using synchronized locks, you should minimize the scope of the lock in order to maximize performance. Do not lock methods if possible. It is recommended to use synchronized code blocks to reduce the scope of locking. Netty source code for example:

// ServerBootstrap.java

public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
    if (childOption == null) {
        throw new NullPointerException("childOption");
    }
    if (value == null) {
        synchronized(childOptions) { childOptions.remove(childOption); }}else {
        synchronized(childOptions) { childOptions.put(childOption, value); }}return this;
}
Copy the code

It can be seen that the critical area of concurrent access code is found, and the whole code is not locked with synchronized, and the use of synchronized to modify methods is avoided as far as possible.

6 summarizes

In this paper, the various uses of synchronized and matters needing attention were briefly combed through the demo. Later, there will be time to discuss the principle of synchronized.