5.2 Synchronization of multiple threads

Thread sleep can be interrupted, through Threadinterrupt0), of course, one thread can call interrupt0) of another thread, and the thread sleep will enter the Runnable state after being interrupted. Since the definition of Thread.sleep( declares that an exception may be thrown in this method through the throw keyword, our program must use a try…catch code block when calling this method, otherwise, the compilation will go wrong, which means It is an aspect of the robustness of the Java language

public class ThreadDemo5{
    public static void main(String[] args){
        ThreadTest t = new ThreadTest();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
}

class ThreadTest implements Runnable{
    private int tickets = 100;
    public void run(){
        while(true){
        if(tickets > 0){
            try {
                Thread. sleep(10);
            }catch(Exception e){
                System.out.println(e.getMessage());
            }
                System.out.println(Thread.currentThread().getName() + "is sale ticket" + tickets--);
            }
        }
    }
}
public class ThreadDemo8{
    public static void main(String[] args){
        ThreadTest t = new ThreadTest();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
}

class ThreadTest implements Runnable{
    private int tickets = 100;
    String str = new String(" ");
    public void run(){
        while(true){
        synchronized(str){
            if(tickets > 0){
            try {
                Thread. sleep(10);
            }catch(Exception e){
                System.out.println(e.getMessage());
            }
                System.out.println(Thread.currentThread().getName() + "is sale ticket" + tickets--);
            }
        }
        
        }
    }
}

Any type of object has a flag bit, which has two states of 0 and 1, and its initial state is 1. When the synchronized (object) statement is executed, the flag bit of the object object becomes 0 until the entire execution is completed. After the code block in the synchronized statement, it returns to the 1 state. When a thread executes to the synchronized (obiect) statement, first check the flag bit of the obiect object. If it is 0, it indicates that there is already another thread executing in the relevant synchronization code block. CPU resources, until another thread executes the relevant synchronization code block, restores the flag bit of the obiect object to 1 state, the block is canceled, the thread can continue to execute, and the flag bit of the obiect object becomes 0 state , to prevent other threads from re-entering the related synchronized code block. If multiple threads are in a blocked state due to waiting for the flag bit of the same object, when the flag bit of the object returns to the 1 state, only one thread can continue to run, and other threads are still in the blocked waiting state. We have repeatedly mentioned the synchronization code block, which means that not only the same code block can be synchronized among multiple threads (like the example above), but also several different code blocks can be synchronized with each other, as long as each synchronized The object in the (object) statement can be exactly the same object. The above explanation is mainly for the purpose of being easy to understand, but sometimes when communicating with colleagues or referring to related books, we also have to master some technical terms. The following is a statement of the content just now in professional terms

When the thread executes to synchronized, check the incoming actual parameter object and get the lock flag of the object (that is, the flag bit we mentioned above). If not, then this thread will be added to a waiting thread pool associated with the object’s lock flag, and wait until the object’s lock flag is returned, and the waiting threads in the pool will get the flag. mark, and then continue to execute. When the thread executes the synchronization code block, it will automatically release the lock flag of the synchronization object it occupies. An object used in a synchronized statement is called a monitor. When a thread obtains the execution right of the code block in the synchronized (object) statement, it means that it locks the monitor. There can only be one thread in a period of time. The monitor can be locked. All other threads will be suspended when trying to enter the locked monitor until the thread that locked the monitor finishes executing the code block in the synchronized (obiect) statement, that is, until the monitor is unlocked, other threads can enter and Lock the monitor. A thread that has just locked the monitor can enter and lock the same monitor again after the monitor is unlocked, just like a basketball player can grab it back again after a basketball is shot. In addition, when a break statement is encountered or an exception is thrown in a synchronized block, the thread will also release the lock flag

In fact, the program cannot control the switching of the CPU, and it is impossible for the program to hold the CPU’s thigh and prevent him from leaving. When the CPU enters a synchronous code block to execute, the CPU can switch to other threads, but when it is ready to execute the code of other threads, it finds that other threads are blocked, and the CPU will return to the previous thread. This process is similar to the god of luck who patronized other related threads, but unexpectedly got rejected and left again.

public class ThreadDemo9{
    public static void main(String[] args){
        ThreadTest t = new ThreadTest();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
}

class ThreadTest implements Runnable{
    private int tickets = 100;
    String str = new String(" ");
    public void run(){
        while(true){
            sale();
        }
    }
    public synchronized void sale(){
        if(tickets > 0){
            try {
                Thread. sleep(10);
            }catch(Exception e){
                System.out.println(e.getMessage());
            }
                System.out.println(Thread.currentThread().getName() + "is sale ticket" + tickets--);
            }
    }
}

In the same class, several methods defined with the synchronized keyword can be synchronized among multiple threads. When a thread enters a method modified by synchronized (obtaining a monitor), other threads cannot enter all methods of the same object. The synchronized modified method is used until the first thread executes the synchronized modified method it entered (leaves the monitor)

public class ThreadDemo01{
    public static void main(String[] args){
        ThreadTest t = new ThreadTest();
        new Thread(t).start();
        t.str = new String("method");
        new Thread(t).start();
    }
}
class ThreadTest implements Runnable{
    private int tickets=100;
    String str = new String(" ");
    public void run(){
        if(str.equals("method")){
            while(true){
                sale();
            }
        }else{
            synchronized(str){
                if(tickets>0){
                    try {
                        Thread. sleep(10);
                    }catch(Exception e){
                        System.out.println(e.getMessage());
                    }
                    System.out.println(Thread.currentThread().getName() + "is sale ticket" + tickets--);
                }
            }
        }
    }
    public synchronized void sale(){
        if(tickets > 0){
            try {
                Thread. sleep(10);
            }catch(Exception e){
                System.out.println(e.getMessage());
            }
                System.out.println(Thread.currentThread().getName() + "is sale ticket" + tickets--);
            }
    }
}

If a thread has just executed the data[idx]=c statement in the push method, the CPU switches to another thread to execute the push method, and the second thread will overwrite the result of the datalidx]=c statement executed by the first thread

In addition, the data shared and accessed should be the private data member of the class, so as to prevent random access from outside the class from destroying the consistency of the data

In actual projects, there will be many multi-thread safety issues. Be very careful when multi-threaded accesses shared data. Errors in thread synchronization are very subtle and cannot be found immediately, and are extremely difficult to troubleshoot. For a large program, if you don’t think carefully in advance, it will be even more difficult and time-consuming to search after the program is written and the instability of the program is found during operation. As long as the program does not have multi-thread safety issues, synchronization technology should not be used, because the source program calls the synchronization method, which requires additional monitor checks, and the operating efficiency is lower.

A deadlock is a rare and difficult to debug bug that occurs when two threads have a circular dependency on two synchronization objects. For example, if a thread enters the monitor of object X, and another object enters the monitor of object y, if the thread entering the monitor of object X tries to enter the monitor of object Y, it will be blocked, and then enter the object of y The monitor’s thread is also blocked if it tries to enter the X object’s monitor, so that both threads are suspended. The most obvious characteristic of a program after a deadlock is that the running of the program is in a state of stagnation. It’s like two people eating, A gets a chopstick and a knife, B gets a fork and a chopstick, neither of them can eat

class A{
    synchronized void foo(B b){
        String name = Thread. currentThread(). getName();
        System.out.println(name + "entered A.foo");
        try {
            Thread. sleep(1000);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        System.out.println(name + "trying to call B.last()");
        b. last();
    }
    synchronized void last(){
        System.out.println("inside A.last");
    }
}
class B{
    synchronized void bar(A a){
        String name = Thread. currentThread(). getName();
        System.out.println(name + "entered B.bar");
        try {
            Thread. sleep(1000);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        System.out.println(name + "trying to call A.last");
        a. last();
    }
    synchronized void last(){
        System.out.println("inside A.last");
    }
}
class Deadlock implements Runnable{
    A a=new A();
    B b = new B();
    Deadlock(){
        Thread.currentThread().setName("MainThread");
        new Thread(this).start();
        a. foo(b);
        System.out.println("back in main thread");
    }
    public void run(){
        Thread.currentThread().setName("RacingThread");
        b. bar(a);
        System.out.println("back in other thread");
    }
    public static void main(String[] args){
        new Deadlock();
    }
}