Deep understanding of synchronized

To understand this problem, we must first know what is synchronized.
In a multithreaded environment, there will be some problems when multiple threads access shared resources at the same time, and the synchronized keyword is used to ensure thread synchronization.
Let's look at a case first

For these 10 threads, we want them to implement. For each thread, I want them to accumulate 9999999 times. The result should be 10 * 9999999. However, according to the actual situation, it is not the result, and the results are inconsistent every time you run. You can try it yourself. It's worth thinking. Why???

synchronized

Let's discuss the security of shared data:
When a thread is running, it will have its own stack space and run on its own stack
 Space running, if there is no shared data between multiple threads, that is
 To put it bluntly, if multithreads do not work together to accomplish one thing, the existence of multithreads is meaningless, but if there is shared data, how should we deal with the thread safety of shared data??????
Let's have a deep discussion here. We naturally think of it along the way
 Let each thread read and write the shared variable in turn, so that there will be no problem of data security.
Because each thread operates on the latest version data. So,
stay java keyword synchronized It has the function of queuing each thread in turn
 The ability to manipulate shared variables. Obviously, this synchronization mechanism is inefficient,
but synchronized It is the basis of other concurrent container implementations
 The solution will also greatly improve the feeling of concurrent programming. From a utilitarian point of view,
This is also the test site of high frequency interview. Well, let's talk about this in detail
 keyword.


package day30.teacher;

public class SellTicketDemo1 implements Runnable {
    private static int count = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new SellTicketDemo1());
            
            thread.start();
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("result: " + count);
    }

    @Override
    public void run() {
        for (int i = 0; i < 9999999; i++)
            count++;
    }
}

Implementation principle

According to the location where synchronized is used, there can be the following usage scenarios: it can be seen that synchronized can be used in methods and code blocks, which are described one by one here.

  1. When used in methods, instance methods and static methods are instance objects and objects of this class respectively.
  2. The code block can be divided into three types. However, if the locked class is an object, although new has multiple instance objects, they still belong to one class and the same class will still be locked, that is, the synchronization relationship between threads is guaranteed.
So three questions:
    1,Who is the lock object of the synchronization code block?
        Any object, but the lock object between multiple threads should be the same

    2,Synchronization method?
        take synchronized Keyword on method
        The lock object of the synchronization method is this

    3,Who is the lock object of the static method?
        class File, bytecode file object also belongs to a Object Class (said when reflecting on the last day)
        this class The file cannot be a bytecode file of any class
        Should be run Bytecode file of the class where the method is located

monitor mechanism

The synchronous code block here locks the class object, and there is also a synchronous static method, which still locks the class object of the class.

public class Monitor1 {
    public static void main(String[] args) {
        synchronized (Monitor1.class) {
        }
        method();
    }
 
    private static void method() {
    }
}
in use Synchronized The key to synchronization is to synchronize the objects
 monitor monitor Get when the thread gets monitor Only after
 Continue to execute, otherwise you can only wait. The acquisition process is mutually exclusive
 , that is, only one thread can get it at the same time monitor. above
 of demo After executing the synchronization code block, we will execute another synchronization code block
 Static synchronization method, and the object locked by this method is still this class object,
Does the executing thread still need to obtain the lock? The answer is No
 of When you execute a static synchronization method, there is only one monitorexit finger
 No, No monitorenter Instruction to acquire a lock. This is the weight of the lock
 Accessibility, that is, in the same lock process, the thread does not need to acquire the same lock again.
Synchronized Congenital reentrant. Each object has a count
 After the thread obtains the lock of the object, the counter will be incremented by one, and after the lock is released
 The counter will be decremented by one.

Any object has its own monitor when the object consists of a synchronization block
 Or when the synchronous method of this object is called, the thread executing the method must first
 Get the monitor of the object to enter the synchronization block and synchronization method. If not
 The thread that gets the monitor will be blocked in the synchronization block and the synchronization method
 Entrance to BLOCKED Status (you can see the status of the thread


To access an Object, any thread must first obtain the monitor of the Object. If the acquisition fails, the thread enters the synchronization state and the thread state changes to BLOCKED. When the monitor owner of the Object is released, the thread in the synchronization queue will have the opportunity to re acquire the monitor.

Advantages and disadvantages of synchronization

    Benefits of synchronization:
        It solves the security problem of multithreading

    Disadvantages of synchronization:
        After adding a thread, it is equivalent to adding a lock. You will judge it every time you enter the synchronous code block
        Virtually, it reduces the execution efficiency

Case:

public class TicketWindow1 implements Runnable{
    //Define 100 tickets
    private int tickets = 100;

    private Object obj = new Object();


    @Override
    public void run() {
        while (true){
            synchronized (obj) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "The second is being sold" + (tickets--) + "Ticket");
                }
            }

        }

    }
}



public class SellTicketDemo1 {
    public static void main(String[] args) {
        TicketWindow1 ticketWindow1 = new TicketWindow1();

        //Create three thread objects to simulate three windows
        //Thread(Runnable target, String name)
        //Assign a new Thread object.
        //While creating the thread object, give the thread a name
        Thread t1 = new Thread(ticketWindow1,"Window 1");
        Thread t2 = new Thread(ticketWindow1,"Window 2");
        Thread t3 = new Thread(ticketWindow1,"Window 3");

        t1.start();
        t2.start();
        t3.start();
    }
}

Lock

concept

In fact, the problem of thread synchronization security is not only solved synchronized This
 One method, we also have the second method, that is, locking Lock

The synchronized keyword is introduced above. Through analysis, which code blocks are packaged, but we don't directly see where the Lock is locked or where other threads are released. In order to more clearly express how to Lock and release the Lock, jdk1 After 5, a new Lock object Lock is provided.

method

By looking at the API, we found that Lock is an interface, so we must implement this interface through its subclasses.

    Specific subclasses: Class ReentrantLock
        void lock()  Lock
        void unlock()  Release lock
public class TicketWindow3 implements Runnable {

    //Define 100 tickets
    private int tickets = 100;

    //Create lock object
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            //t1,t2,t3
            lock.lock();
            if (tickets > 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "The second is being sold" + (tickets--) + "Ticket");
            }
            lock.unlock();
        }



    }
}

public class SellTicketDemo3 {
    public static void main(String[] args) {
        TicketWindow3 ticketWindow3 = new TicketWindow3();

        //Create three thread objects to simulate three windows
        //Thread(Runnable target, String name)
        //Assign a new Thread object.
        //While creating the thread object, give the thread a name
        Thread t1 = new Thread(ticketWindow3,"Window 1");
        Thread t2 = new Thread(ticketWindow3,"Window 2");
        Thread t3 = new Thread(ticketWindow3,"Window 3");

        t1.start();
        t2.start();
        t3.start();
    }
}

Keywords: Java Back-end

Added by eXpertPHP on Fri, 31 Dec 2021 22:23:07 +0200