[Multithreading Elementary] Analyzing the lock object in synchronized

  • lock object
  • Several common situations
  • Summarize

Lock object

Let’s first look at how objects are defined in Java. In the Java virtual machine, the structure of objects in memory can be divided into four parts:
markword
Type pointer (_class)
Instance data (instance_data)
Align padding
Among them, the first two are in the object header, the instance data represents the attributes in the class, and the alignment padding means that the number of bytes occupied by the object of each class must be an integer multiple of 8 bytes. Objects in Java include a part called the object header, where the markword area mainly describes which thread currently acquires the lock resource, and records the thread object information. When the thread releases the lock resource, the thread object will be After the information is cleared, other threads can continue to acquire lock resources.

Several common situations

We define two threads to achieve 50,000 self-increment respectively, and use different lock objects to see if the final result can meet expectations.
1. Define an auto-increment operation object to call an auto-increment method, and the lock object to be competed in it is the same this, which can meet expectations at this time.

public class Demo01_Synchronized {<!-- -->
    // Define the object of the auto-increment operation
    private static Counter2 counter = new Counter2();

    public static void main(String[] args) throws InterruptedException {<!-- -->
        // Define two threads, each auto-incremented 50,000 times
        Thread t1 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter. increment();
            }
        });

        Thread t2 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter. increment();
            }
        });

        // start the thread
        t1. start();
        t2.start();
        // wait for auto-increment to complete
        t1. join();
        t2. join();
        // print the result
        System.out.println("count = " + counter.count);

    }
}
class Counter2 {<!-- -->
    public static int count = 0;
    // self-increment method
    public synchronized void increment () {<!-- -->
        count + + ;
    }
}

2. When defining two self-increment operation objects to call the same self-increment method, the this to be competed in is not the same object, and there will be no lock competition, and the expected result will not be obtained.

public class Demo04_Synchronized {<!-- -->
    // Define the object of the auto-increment operation
    private static Counter4 counter = new Counter4();
    private static Counter4 counter1 = new Counter4();

    public static void main(String[] args) throws InterruptedException {<!-- -->
        // Define two threads, each auto-incremented 50,000 times
        Thread t1 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter. increment();
            }
        });

        Thread t2 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter1. increment();
            }
        });

        // start the thread
        t1. start();
        t2.start();
        // wait for auto-increment to complete
        t1. join();
        t2. join();
        // print the result
        System.out.println("count = " + counter.count);

    }
}
class Counter4 {<!-- -->
    public static int count = 0;
    // self-increment method
    public synchronized void increment () {<!-- -->
        count + + ;
    }
}

3. Customize an object to act as the lock object, and use an auto-increment operation object to call the auto-increment method to get the expected result.

public class Demo05_Synchronized {<!-- -->
    // Define the object of the auto-increment operation
    private static Counter5 counter = new Counter5();
    public static void main(String[] args) throws InterruptedException {<!-- -->
        // Define two threads, each auto-incremented 50,000 times
        Thread t1 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter. increment();
            }
        });

        Thread t2 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter. increment();
            }
        });

        // start the thread
        t1. start();
        t2.start();
        // wait for auto-increment to complete
        t1. join();
        t2. join();
        // print the result
        System.out.println("count = " + counter.count);

    }
}
class Counter5 {<!-- -->
    public static int count = 0;
    //Define an object to act as a lock object
    Object locker = new Object();
    // self-increment method
    public void increment () {<!-- -->
        synchronized (locker){<!-- -->
            count + + ;
        }
    }
}

4. On the basis of 3, define two self-increment operation objects to call the self-increment method respectively, but the expected result cannot be obtained. The reason is that the locker object is initialized every time it is a member variable, and the obtained locker object is not the same. For an object, there will be no lock competition.

public class Demo05_Synchronized {<!-- -->
    // Define the object of the auto-increment operation
    private static Counter5 counter = new Counter5();
    private static Counter5 counter1 = new Counter5();
    public static void main(String[] args) throws InterruptedException {<!-- -->
        // Define two threads, each auto-incremented 50,000 times
        Thread t1 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter. increment();
            }
        });

        Thread t2 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter1. increment();
            }
        });

        // start the thread
        t1. start();
        t2.start();
        // wait for auto-increment to complete
        t1. join();
        t2. join();
        // print the result
        System.out.println("count = " + counter.count);

    }
}
class Counter5 {<!-- -->
    public static int count = 0;
    //Define an object to act as a lock object
    Object locker = new Object();
    // self-increment method
    public void increment () {<!-- -->
        synchronized (locker){<!-- -->
            count + + ;
        }
    }
}

5. On the basis of 4, modify the locker object with static, and you can get the expected result at this time. The reason is that the statically modified class members are globally unique when the class is initialized, so that each new object of the self-increment operation accesses the same locker, there is a lock competition relationship, and the correct result can be obtained.

public class Demo05_Synchronized {<!-- -->
    // Define the object of the auto-increment operation
    private static Counter5 counter = new Counter5();
    private static Counter5 counter1 = new Counter5();
    public static void main(String[] args) throws InterruptedException {<!-- -->
        // Define two threads, each auto-incremented 50,000 times
        Thread t1 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter. increment();
            }
        });

        Thread t2 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                counter1. increment();
            }
        });

        // start the thread
        t1. start();
        t2.start();
        // wait for auto-increment to complete
        t1. join();
        t2. join();
        // print the result
        System.out.println("count = " + counter.count);

    }
}
class Counter5 {<!-- -->
    public static int count = 0;
    //Define an object to act as a lock object
    private static Object locker = new Object();
    // self-increment method
    public void increment () {<!-- -->
        synchronized (locker){<!-- -->
            count + + ;
        }
    }
}

6. Using a class object as a lock object will also cause lock competition, because the class object is globally unique.

public class Demo06_Synchronized {<!-- -->

    // Define the object of the auto-increment operation
    private static Counter6 counter = new Counter6();
    private static Counter6 counter1 = new Counter6();

    public static void main(String[] args) throws InterruptedException {<!-- -->
        // Define two threads, each auto-incremented 50,000 times
        Thread t1 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                // Call the locking method
                counter. increment();
            }
        });

        Thread t2 = new Thread(() -> {<!-- -->
            for (int i = 0; i < 50000; i ++ ) {<!-- -->
                // call method without lock
                counter1. increment();
            }
        });

        // start the thread
        t1. start();
        t2.start();
        // wait for auto-increment to complete
        t1. join();
        t2. join();
        // print the result
        System.out.println("count = " + counter.count);

    }
}

class Counter6 {<!-- -->
    public static int count = 0;

    public void increment() {<!-- -->
        // Use the Counter6 class object as the lock object
        synchronized (Counter6. class) {<!-- -->
            // Modification logic to be executed
            count + + ;
        }
    }
// public void increment() {<!-- -->
// // Use the String class object as the lock object
// synchronized (String. class) {<!-- -->
// // Modification logic to be executed
// count + +;
// }
// }
}

Summary

In a multi-threaded environment, it is enough to ensure that the lock object competing among multiple threads is the same, and the lock object can be any object in Java. It is important to understand, what is the synchronized lock? Two threads competing for the same lock will cause blocking waiting; Two threads try to acquire two different locks respectively, and there will be no competition.

Keep going~