1. The meaning of Synchronized

The Synchronized keyword is Java’s solution to the thread-safe problem of accessing shared variables between multiple threads. It is a blocking solution that ensures thread-safe access to a critical region by only one thread.

Here is an example of thread insecurity:

/** * Share problems *@authorSunset is not lonely
@Slf4j(topic = "c.Test17")
public class Test17 {
    static int counter = 0;
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) { counter++; }},"t1");

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) { counter--; }},"t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        log.debug("{}", counter); }}Copy the code

There are two threads, one of which increments the shared variable counter and the other decrement. After many runs, you can see that the results are unstable, sometimes integer, sometimes negative or 0. This happens because the addition or decrement of the shared variable counter is not atomic. Here is the bytecode of the variable counter++ in the JVM:

getstatic counter // Get the value of the static variable counter
iconst_1 // Prepare constant 1
iadd / / since the increase
putstatic counter // Store the modified value into the static variable counter
Copy the code

If t1 is executed first and the value of counter (0) is obtained, the auto-increment operation is performed, and there is no time to write, then the thread context switch occurs. So t2 gets counter 0, subtracts, and says counter, counter = -1, and t2 is done. T1 thread turns to execute, finds the statement to execute according to the program counter, writes the last increment result 1, and completes execution. We can see that the result is 1 instead of the expected 0. The same thing happens with negative numbers, so I’m not going to do it here.

Why did this happen? You’re smart enough to know that access to shared variables is not atomic. We just have to make sure that the two threads are accessing the shared variable synchronously. It’s like having a room that two people want to live in, but only one can live in. Nobody wants to be disturbed. All we have to do is put a lock on the door. When we stopped, we unlocked it and gave it to someone else. Java implements this mechanism with the keyword syschronized. The lock object is arbitrary, but all threads accessing the same shared variable use the same lock.

2. Use and precautions of Synchronized

use

Basic syntax:

synchronized(object) {critical section}Copy the code

Three ways of use:

  1. Load synchronized code blocks
class Test{
    public void test(a) {
        synchronized(obj) {
        }
    }
} 
Copy the code
  1. Plus the normal method
class Test {
    public synchronized void test(a) {}} is equivalent toclass Test{
    public void test(a) {
        synchronized(this) {}}}Copy the code
  1. To static methods
class Test{
    public synchronized static void test(a) {}} is equivalent toclass Test{
    public static void test(a) {
        synchronized(Test.class) {
        }
    }
}
Copy the code

Matters needing attention

When using synchronized, pay attention to whether the thread is using the same lock object. Keep in mind that thread safety is not guaranteed if it is not the same thread. We can also use this feature to quickly determine whether a program is thread safe.

Decompilation of bytecode

First, prepare the Java source file that needs to be decomcompiled. You can directly decomcompile bytecode through Java naming (JDK environment variables need to be configured).

  1. Compile Java source files
javac fileName.java
Copy the code
  1. Decompile the generated bytecode
javap -c fileName
Copy the code

If you need to save the decompiled results to a file, you can use redirection techniques.

javap -c fileName > testFile.txt
Copy the code