Synchronized brief introduction to "usage"

Keywords: Java Back-end

Two uses of Synchronized

Object lock

It includes method lock (the default lock object is this, that is, the current instance object) and synchronous code block lock (specify the lock object yourself)

Class lock

A Synchronized method that modifies a static or specifies a lock as a Class object

Example

No synchronization effect

public class DemoCode1 implements Runnable {
    private static DemoCode1 instance = new DemoCode1();

    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    @Override
    public void run() {
        System.out.println("This is Thread" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread" + Thread.currentThread().getName() + " finished.");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()) {
            ;
        }
        System.out.println("finished");
    }
}

The operation result is:

This is ThreadThread-0
This is ThreadThread-1
ThreadThread-1 finished.
ThreadThread-0 finished.
finished

Object locks synchronize code blocks and method locks

Synchronous code block form

Use the synchronization code block in the above code

    @Override
    public void run() {
        synchronized (this) {
            System.out.println("This is Thread" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread" + Thread.currentThread().getName() + " finished.");
        }
    }

Operation results

This is ThreadThread-0
ThreadThread-0 finished.
This is ThreadThread-1
ThreadThread-1 finished.
finished

This code uses the this lock object. If it is changed to the object form, its running results are consistent.

    private Object lock1 = new Object();

    @Override
    public void run() {
        synchronized (lock1) {
            System.out.println("This is Thread" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread" + Thread.currentThread().getName() + " finished.");
        }
    }

Object locking enables us to use multiple locks in one method, such as:

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    @Override
    public void run() {
        synchronized (lock1) {
            System.out.println("lock1 Object lock code block 1 " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Code block 1 " + Thread.currentThread().getName() + " end.");
        }
        synchronized (lock2) {
            System.out.println("lock2 Object lock code block 2 " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Code block 2" + Thread.currentThread().getName() + " end.");
        }
    }

The first code uses the lock1 object to lock, and the second code uses the lock2 object. In this way, the locks obtained by the two pieces of code are different, that is, the two pieces of code are not allowed to be executed at the same time, but the method can execute the two pieces of code at the same time. The operation result is:

lock1 object lock code block 1 Thread-0
End of code block 1 Thread-0
lock2 object lock code block 2 Thread-0
lock1 object lock code block 1 Thread-1
Code block 2Thread-0 ends
End of code block 1 Thread-1
lock2 object lock code block 2 Thread-1
Code block 2Thread-1 ends
finished

It can be seen from the output results that because the methods are executed sequentially, when lock1 executes to code block 1, the first section of code only allows lock1 object lock execution. When lock1 executes the first section of code, it will continue to execute code block 2; At the same time, lock2 obtains the lock of code block 1, and lock2 also starts to execute the first section of code. The execution logic of code block 2 is the same as that of code block 1.

Method lock form

The method lock form needs to add Synchronized on the method declaration. If it is not added, the execution result is no synchronization effect. The code added to the method is as follows

public class DemoCode2 implements Runnable{
    private static DemoCode2 instance = new DemoCode2();

    public synchronized void method() {
        System.out.println("Method lock code block " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Code block " + Thread.currentThread().getName() + " end.");
    }

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

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()) {
            ;
        }
        System.out.println("finished");
    }
}

The operation result is

Method lock code block Thread-1
Method lock code block Thread-0
Code block Thread-1 ends
Code block Thread-0 ends
finished

summary

  1. Synchronizing code blocks requires you to manually specify the lock object

  2. Method lock defaults to this as the lock object

Class lock

A Java Class may have many instance objects, but only one Class object.

There are two types of locks:

  1. Synchronized is added to the static method

  2. Synchronized(*.class) code block

The so-called Class lock is actually just the lock of the Class object.

Example

No synchronization effect

public class DemoCode3 implements Runnable {
    private static DemoCode3 instance1 = new DemoCode3();
    private static DemoCode3 instance2 = new DemoCode3();

    public static void method() {
        System.out.println("Method lock code block " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Code block " + Thread.currentThread().getName() + " end.");
    }

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

    public static void main(String[] args) {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()) {
            ;
        }
        System.out.println("finished");
    }
}

because

        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);

Two different objects are used, so for two threads, their this points are different, and the obtained locks are not the same lock. The result of running will not produce the effect of synchronization, and the running result is

Method lock code block Thread-1
Method lock code block Thread-0
Code block Thread-0 ends
Code block Thread-1 ends
finished

static method plus Synchronized

Add the method method in the above code to the synchronization keyword

    public synchronized static void method() {
        System.out.println("Method lock code block " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Code block " + Thread.currentThread().getName() + " end.");
    }

Because the method is static, it means that the method does not belong to an instance object, but to this Class. Therefore, the obtained this actually points to the Class object of this Class. Since each Class has only one Class object, the obtained lock is actually the same. Then synchronization will occur during operation, and the operation result is

Method lock code block Thread-0
Code block Thread-0 ends
Method lock code block Thread-1
Code block Thread-1 ends
finished

Synchronous code block

public class DemoCode4 implements Runnable{
    private static DemoCode4 instance1 = new DemoCode4();
    private static DemoCode4 instance2 = new DemoCode4();

    public synchronized static void method() {
        synchronized (DemoCode4.class) {
            System.out.println("Class lock code block 1 " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Class lock " + Thread.currentThread().getName() + " end.");
        }
    }

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

    public static void main(String[] args) {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()) {
            ;
        }
        System.out.println("finished");
    }
}

The principle is the same as the above description. It is essentially an object lock, but because the class object is unique, the execution result of this code is still synchronous.

Several cases of multithreaded access synchronization methods

Synchronization method for two threads accessing the same object

Since the synchronization method of the same object is accessed, we can know from the above contents that the lock obtained by the synchronization method is this by default, because the two threads access the same object, that is, this points to the same, that is, they have the same lock. Then this situation will be synchronized normally.

Two threads access the synchronization method of two objects

Two objects are accessed. Since the default lock of the synchronization method is this, you can know that the locks they obtain are not the same. In this case, synchronization will not occur.

The two threads access the Synchronized static method

Because static methods are accessed, static methods belong to classes. A Class has only one Class object, so this of the static synchronization method still points to the same object, which will run synchronously normally.

Simultaneous access to synchronous and asynchronous methods

In this case, the two methods need to be treated separately. The implementation of the synchronous method is mentioned above, while the non synchronous method will not wait at all. Therefore, the execution effect of this situation is still that the synchronization method will synchronize normally, but the non synchronization method will synchronize at the same time because it has no synchronization attribute.

Different non static synchronization methods for accessing the same object

Since the non static synchronization method of the same object is accessed, the default lock of the synchronization method is this. However, since the method is not static, this points to the instance object rather than the Class object. Since it is a method of the same object, this also points to the same instance object, so this is still a normal synchronization.

Access both static and non static synchronization methods

The idea is the same. The object pointed to by the static method is a Class object, and the non static method points to this. The two methods do not point to the same lock object, so there will be no synchronization effect. The two methods will be carried out at the same time.

Method releases the lock after it throws an exception

If you throw an exception without releasing the lock, because the exception throwing method will not continue to execute, other threads will never get the lock. Therefore, if one of the threads throws an exception when running the method, the lock owned by the thread will be automatically released. It should be noted that the order must be to throw an exception first and then release the lock.

Posted by mike_y on Sat, 30 Oct 2021 23:11:35 -0700