Make writing a habit together! This is the second day of my participation in the “Gold Digging Day New Plan · April More text challenge”. Click here for more details.

In Java, the main means to ensure thread safety is locking, and there are two main types of locking in Java: synchronized and Lock. Today we focus on several uses of synchronized.

Introduction of usage

Synchronized does not need to manually lock and release the lock. Simply declare the synchronized keyword, and the JVM automatically locks and releases the lock. Synchronized can be used to modify normal methods, static methods, and code blocks, as we’ll see separately.

1. Modify common methods

Synchronized modifies common methods as follows:

/** * synchronized
public synchronized void method(a) {
    / /...
}
Copy the code

When synchronized modifies ordinary methods, the modified method is called a synchronized method, and its scope is the entire method, acting on the object that invoked the method.

2. Modify static methods

Synchronized is a static method that is similar to synchronized. It is used as follows:

/** * synchronized static methods */
public static synchronized void staticMethod(a) {
    / /...
}
Copy the code

When synchronized modifies static methods, its scope is the entire program, and the lock is mutually exclusive to all objects that invoke it.

Mutual exclusion means that only one thread can use it at a time, and other threads must wait in a queue.

Modify normal methods VS static methods

Synchronized modifies ordinary methods and static methods, but they are completely different. For static methods, synchronized locking is global, that is, all objects that call the static method are mutually exclusive during the entire program run, while ordinary methods are targeted at the object level. Different objects correspond to different locks, such as the following code, the same is called twice, but the lock is completely different, the implementation code is as follows:

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedUsage {
    public static void main(String[] args) throws InterruptedException {
        // Create a thread pool to execute tasks simultaneously
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        // Execute static methods twice
        threadPool.execute(() -> {
            staticMethod();
        });
        threadPool.execute(() -> {
            staticMethod();
        });
        
        // Execute the normal method twice
        threadPool.execute(() -> {
            SynchronizedUsage usage = new SynchronizedUsage();
            usage.method();
        });
        threadPool.execute(() -> {
            SynchronizedUsage usage2 = new SynchronizedUsage();
            usage2.method();
        });
    }

    This method requires 3s (because there is a 3s sleep time) */
    public synchronized void method(a) {
        System.out.println("Normal method execution time:" + LocalDateTime.now());
        try {
            / / sleep 3 s
            TimeUnit.SECONDS.sleep(3);
        } catch(InterruptedException e) { e.printStackTrace(); }}This method requires 3s (because there is a 3s sleep time) */
    public static synchronized void staticMethod(a) {
        System.out.println("Static method execution time:" + LocalDateTime.now());
        try {
            / / sleep 3 s
            TimeUnit.SECONDS.sleep(3);
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

The execution results of the above procedures are as follows:As can be seen from the above results,Static method locking is global and applies to all callers; Normal method locking is at the object level, and different objects have different locks.

3. Modify the code block

In daily development, we usually lock code blocks instead of methods, because locking methods is equivalent to locking the whole method. In this way, the granularity of locking is too large, and the performance of the program will be affected. Therefore, synchronized is usually used to lock code blocks. Its implementation syntax is as follows:

public void classMethod(a) throws InterruptedException {
    // Preloading code...
    
    // Lock code
    synchronized (SynchronizedUsage.class) {
        / /...
    }
    
    // postcode...
}
Copy the code

As we can see from the above code, the decorator code block, in contrast to the decorator method, needs to manually specify the lock object itself. The lock object is usually represented by the form this or xxx.class, as in the following code:

// Lock a class
synchronized (SynchronizedUsage.class) {
    / /...
}

// Lock the current class object
synchronized (this) {
    / /...
}
Copy the code

this VS class

Synchronized locks this and xxx.class are completely different. When synchronized locks this, it means that the current object is used to lock, and each object has a corresponding lock. When xxx.class is used, it means that a class (rather than an instance of the class) is used to lock, which is application-level and global, as shown in the following code:

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedUsageBlock {
    public static void main(String[] args) throws InterruptedException {
        // Create a thread pool to execute tasks simultaneously
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        // synchronized(this)
        threadPool.execute(() -> {
            SynchronizedUsageBlock usage = new SynchronizedUsageBlock();
            usage.thisMethod();
        });
        threadPool.execute(() -> {
            SynchronizedUsageBlock usage2 = new SynchronizedUsageBlock();
            usage2.thisMethod();
        });

        Synchronized (xxx.class)
        threadPool.execute(() -> {
            SynchronizedUsageBlock usage3 = new SynchronizedUsageBlock();
            usage3.classMethod();
        });
        threadPool.execute(() -> {
            SynchronizedUsageBlock usage4 = new SynchronizedUsageBlock();
            usage4.classMethod();
        });
    }

    ** * synchronized(this) */ ** synchronized(this) */
    public void thisMethod(a) {
        synchronized (this) {
            System.out.println("Synchronized (this) : + LocalDateTime.now());
            try {
                / / sleep 3 s
                TimeUnit.SECONDS.sleep(3);
            } catch(InterruptedException e) { e.printStackTrace(); }}}** * synchronized(xxx.class) */ ** * synchronized(xxx.class) */
    public void classMethod(a) {
        synchronized (SynchronizedUsageBlock.class) {
            System.out.println(Synchronized (xxx.class) synchronized: + LocalDateTime.now());
            try {
                / / sleep 3 s
                TimeUnit.SECONDS.sleep(3);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

The execution results of the above procedures are as follows:

conclusion

Synchronized can be used to modify ordinary methods, static methods and code blocks, among which the most common is to modify code blocks, and to modify code blocks requires specifying a lock object, which is usually represented by this or xxx.class. When this is used, Class means that the current object is used to lock, whereas class means that a class (non-class object instance) is used to lock, which is global.

Judge right and wrong from yourself, praise to listen to others, gain and loss in the number.

Public number: Java interview analysis

Interview collection: gitee.com/mydb/interv…