Multi-threading from entry to advanced (2)-basic use of sync lock

Reference: "Java Multithreaded Programming Core Technology"

One, thread safety and non-thread safety

  • Non-thread-safe: When multiple threads concurrently access instance variables in the same object, the result is dirty reads (here we use the database example to explain dirty reads. The things here are equivalent to the run method in the thread. Rolling is when the run method in the thread is executed)

Dirty read: Dirty data before the rollback of another transaction is read. For example, during the execution of transaction B, data X is modified. Before committing, transaction A reads X, but transaction B rolls back. In this way, transaction A forms a dirty read.

Non-repeatable read: Transaction A first reads a piece of data, and then when the logic is executed, transaction B changes this piece of data, and then when transaction A reads it again, it finds that the data does not match, which is the so-called non-repeatable read .

Phantom reading: Transaction A first obtains N pieces of data according to the condition index, and then transaction B changes M items other than these N pieces of data or adds M pieces of data that meet the search conditions of transaction A, causing transaction A to search again and find that there are N+ With M pieces of data, phantom reading occurs.

  • Thread safety: The value of the obtained instance variable is processed synchronously, and the variables in the method are thread-safe

Two, synchronized synchronization method

Let's look at an example:

public class HasSelfPrivateNum {
    
    
    private int num = 0;
    public void addI(String username){
    
    
        try {
    
    
            if(username.equals("a")){
    
    
                num = 100;
                System.out.println("a set over");
                Thread.sleep(2000);
            }else{
    
    
                num = 200;
                System.out.println("b set over");
            }
            System.out.println("username= " + username + " num=" + num);
        }catch (InterruptedException e){
    
    
            e.printStackTrace();
        }

    }

}
public class ThreadA extends Thread{
    
    
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRef) {
    
    
        this.numRef = numRef;
    }
    @Override
    public void run() {
    
    
        numRef.addI("a");
    }
}
public class ThreadB extends Thread{
    
    
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRef) {
    
    
        this.numRef = numRef;
    }

    @Override
    public void run() {
    
    
        numRef.addI("b");
    }
}
public class Run {
    
    
    public static void main(String[] args) {
    
    
        HasSelfPrivateNum numRef = new HasSelfPrivateNum();
        new ThreadA(numRef).start();
        new ThreadB(numRef).start();
    }
}

HasSelfPrivateNum is a business operation method. When the incoming username is "a", num=100 and print "a set over". When the incoming username is "b", num=200 and print "b" set over", ThreadA and ThreadB are two threads, Run is the main thread

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-6R71FfPv-1615786729413) (C:\Users\VSUS\Desktop\Notes\Multithreading\img\15.png )]

Analysis: When thread A and thread B start, thread A first preempts the CPU, executes the addI of HasSelfPrivateNum, sets num=100, and then executes Thread.sleep(2000);, thread A is dormant, then thread B gets the CPU, enter the method and set num=200, and print username=b num=200. When thread A wakes up, it does not know the value of num and is changed to num=200, so it outputs "username= a num" =200"

You only need to add a synchronized lock to the addI method

public class HasSelfPrivateNum {
    
    
    private int num = 0;
    synchronized public void addI(String username){
    
    
        ......
    }
}

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-t4JNC7Bv-1615786729416)(C:\Users\VSUS\Desktop\Notes\Multithread\img\16.png )]

Analysis: When thread A and thread B are started, thread A first preempts the CPU and obtains the lock of the current object, executes the addI of HasSelfPrivateNum, sets num=100, and then executes Thread.sleep(2000);, thread A In a dormant state, thread B wants to execute addI at this time, but finds that it has not obtained the lock of the object and can only wait. When thread A finishes executing the addI method, the lock is released, and thread B starts to get the lock and execute addI

1. Multiple objects and multiple locks cannot be synchronized

Change the above run method to the following:

public class Run {
    
    
    public static void main(String[] args) {
    
    
        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
        new ThreadA(numRef1).start();
        new ThreadB(numRef2).start();
    }
}

The results of the operation are:

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-rjXJIVhV-1615786729419) (C:\Users\VSUS\Desktop\Notes\Multithreading\img\17.png )]

It can be seen that the order of operation is crossed, not synchronized. Synchronized locks are not code, but objects. If multiple threads access multiple objects, the JVM will create multiple locks, so the result of execution is asynchronous.

2.Syncorized lock reentry

Reentrant lock: You can acquire your own internal lock again. For example, if a thread acquires the lock of an object, if the object lock has not been released, then the object can obtain the object lock.

public class Service {
    
    
    synchronized public void service1(){
    
    
        try{
    
    
            System.out.println("begin service1 threadName=" + Thread.currentThread().getName() + " time"
                    + System.currentTimeMillis()
            );

            System.out.println("service1..");
            Thread.sleep(2000);
            System.out.println("end service1 threadName=" + Thread.currentThread().getName() + " time"
                    + System.currentTimeMillis()
            );
            service2();
        }catch (InterruptedException e){
    
    
            e.printStackTrace();
        }

    }
    synchronized public void service2(){
    
    
        try{
    
    
            System.out.println("begin service2 threadName=" + Thread.currentThread().getName() + " time"
                    + System.currentTimeMillis()
            );

            System.out.println("service2...");
            Thread.sleep(2000);
            System.out.println("end service2 threadName=" + Thread.currentThread().getName() + " time"
                    + System.currentTimeMillis()
            );
        }catch (InterruptedException e){
    
    
            e.printStackTrace();
        }

    }

}
public class MyThread2 extends Thread{
    
    
    private Service service;
    public MyThread2(Service service) {
    
    
        this.service = service;
    }

    @Override
    public void run() {
    
    
        service.service2();
    }
}
public class Run {
    
    
    public static void main(String[] args) {
    
    
        Service service = new Service();
        new MyThread1(service).start();
    }
}

In the Service class, the methods service1 and service2 that are all modified by synchronized are created respectively. Service1 calls service2, and MyThread1 creates a thread, which is started by the main of the Run class. The conclusion to be proved is that the thread can obtain the lock of the object again, that is The service2 method will be executed

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-RCbvcsSM-1615786729425)(C:\Users\VSUS\Desktop\Notes\Multithread\img\18.png )]

But this is not enough to prove that even if the thread acquires the lock, create a thread MyThread2 again

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-rDKGpUZE-1615786729426)(C:\Users\VSUS\Desktop\Notes\Multithread\img\19.png )]

It can be seen that thread 1 and thread 2 are executed asynchronously, because thread 1 still holds the lock of the object when executing the service2 method, so thread 2 can only wait.

So if a thread acquires the synchronized lock, then it is known from the above that other threads cannot call the method locked by the Lock again. Can you call the method that is not locked by the synchronized? Modify the above code: remove the synchronized method of service2 and remove the sleep method of service2. Thread 1 calls the service1 method, and Thread 2 calls the service2 method.

public class Service {
    
    
    synchronized public void service1(){
    
    
        ......
    }
     public void service2(){
    
    
		......
            //Thread.sleep(2000);
		......
            );
    }

}
public class MyThread1 extends Thread{
    
    
    private Service service;
    public MyThread1(Service service) {
    
    
        this.service = service;
    }

    @Override
    public void run() {
    
    
        service.service1();
    }
}
public class MyThread2 extends Thread{
    
    
    private Service service;
    public MyThread2(Service service) {
    
    
        this.service = service;
    }

    @Override
    public void run() {
    
    
        service.service2();
    }
}

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-m2XKTp5X-1615786729428) (C:\Users\VSUS\Desktop\Notes\Multithreading\img\20.png )]

It can be seen that thread 1 and thread 2 are executed asynchronously, that is, thread 2 can call methods that are not locked by the sync lock

Analysis of the above execution process: Thread 1 starts first and gets the lock, and starts to call the service1 method. When it encounters the sleep method, it is in sleep state. Thread 2 is allocated to the time slice by the CPU and starts to call the service2 method because service2 is not synchronized. Modification, so thread 2 can call the service2 method, and wait until thread 2 finishes executing the service2 method. After thread 1 wakes up, it finishes executing the post-sequence part of the service1 method, and it ends!

Important conclusion:

When the A thread calls the X method of the anyObject object with the synchronized keyword, the A thread obtains the lock of the object where the X method is located, so other threads must wait for the A thread to complete before calling the X method, and if the B thread calls the statement The non-X method with the synchronized keyword must wait for the A thread to execute the X method, that is, it can be called after the lock is released

Note: When there is a parent-child class inheritance relationship, the child class can call the synchronization method of the parent class through the "reentrant lock", but synchronization is not inherited

Three, synchronized synchronization statement block

Using the keyword synchronized to declare the method is caused by drawbacks in some cases. For example, the A thread calls the synchronization method to perform a long-term task, then the B thread must wait a long time, then using synchronized to only lock some may cause non The code block for thread safety issues will improve a certain degree of efficiency. In addition, the object itself is locked using the synchronized method. This causes that if there is only one object instance, it can only be executed asynchronously, and use Synchronized statement blocks can be executed synchronously without thread safety issues

public class Task {
    
    
    public void doLongTime(){
    
    
        for (int i = 1; i <= 100; i++) {
    
    
            System.out.println("noSynchronized threadName = " + Thread.currentThread().getName() + " i=" + i);
        }
        synchronized (this){
    
    
            for (int i = 1; i <= 100; i++) {
    
    
                System.out.println("Synchronized threadName = " + Thread.currentThread().getName() + " i=" + i);
            }
        }
    }

}
public class MyThread implements Runnable {
    
    
    private Task task;

    public MyThread(Task task) {
    
    
        this.task = task;
    }

    @Override
    public void run() {
    
    
        task.doLongTime();
    }
}
public class Run {
    
    
    public static void main(String[] args) {
    
    
        Task task = new Task();
        Thread t1 = new Thread(new MyThread(task));
        t1.start();
        Thread t2 = new Thread(new MyThread(task));
        t2.start();
    }
}

Create a task class to simulate a long-term task, create two processes, after startup, it is found that the code block not enclosed by synchronized is executed asynchronously, and the code enclosed by synchronized is executed synchronously

[External link image transfer failed. The source site may have an anti-hotlink mechanism. It is recommended to save the image and upload it directly (img-wI16ulzi-1615786729431)(C:\Users\VSUS\Desktop\Notes\Multithread\img\21.png )]

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-jKP5TkS0-1615786729432)(C:\Users\VSUS\Desktop\Notes\Multithread\img\22.png )]

Use synchronized(this) to lock the current object

synchorinzed(x), where x can be any object, and 3 conclusions can be drawn

  • When multiple threads execute synchorinzed(x) synchronized code blocks at the same time, it shows a synchronization effect
  • When other threads execute the synchorinzed synchronous method in the x object, it also shows a synchronous effect. The
    living code block is executed asynchronously, and the code enclosed by synchronized is executed synchronously.

[External link image is being transferred...(img-wI16ulzi-1615786729431)]

[External link image is being transferred...(img-jKP5TkS0-1615786729432)]

Use synchronized(this) to lock the current object

synchorinzed(x), where x can be any object, and 3 conclusions can be drawn

  • When multiple threads execute synchorinzed(x) synchronized code blocks at the same time, it shows a synchronization effect
  • When other threads execute the synchorinzed synchronization method in the x object, it is also synchronized.
  • When other threads execute the synchorinzed(this) code block in the x object method, it also has a synchronization effect.

Guess you like

Origin blog.csdn.net/weixin_44706647/article/details/114829841