Thread safety and thread state

Posted by boofboof on Tue, 04 Jan 2022 22:43:05 +0100

 1. Thread state

Threads have five statuses: new; be ready; Obstruction; function; death

New state: a new thread state created by the new Thread() method

Ready state: enter the ready state after starting through the start () method. It is not executed immediately and waits for CPU scheduling

Blocking state: use the sleep() method or synchronization lock to convert the thread from running state to blocking state

Running status: the thread obtains CPU scheduling and starts execution

Dead state: the thread enters the dead state after execution

The state transition diagram is:

The join() method can also cause a thread to enter a blocking state from running

public class Text {
    public static void main(String[] args) throws InterruptedException {
        RunnableDome runnableDome=new RunnableDome();
        //Create an object that implements the class
        Thread thread=new Thread(runnableDome);
        thread.join();
        //Add this thread to the current thread, that is, after the thread executed by the main method is executed, execute the thread thread
        //Pass the object implementing the class as a parameter into the Thread object
        thread.start();
        
    }
}

The sleep () method code is implemented as follows:

public class TicketThread extends Thread {
    static int number=10;
    static Object obj=new Object();
    @Override
    public void run() {
        while (true){
            synchronized (obj){
                try {
                    Thread.sleep(1000);
//Put the program into sleep state, and the sleep () method parameter is milliseconds
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (number > 0) {
                    System.out.println(Thread.currentThread().getName()+"Got a ticket"+number);
                    number--;
                }
                else {
                    break;
                }
            }
        }

    }

 2. Thread safety

2.1 synchronized keyword (implicit synchronization)

There are two main reasons for thread safety: one is the competition of shared resources, the other is that multiple threads operate shared resources together; The emergence of synchronized is to meet the needs of solving such problems. In java, sycchronized can be implemented. At the same time, only one thread can execute a method or code block. After this thread enters the shared resource, it will be locked automatically, and other threads will wait outside. After execution, it will be unlocked automatically. This process is implicit. The three application modes of synchronized are:

Here is a test case for buying tickets. The test classes of the three application modes are the same, and the code implementation is as follows:
 

public class Text {
    public static void main(String[] args) {
        TicketThread ticketThread1= new TicketThread();
        ticketThread1.setName("Window 1");
        ticketThread1.start();
        TicketThread ticketThread2=new TicketThread();
        ticketThread2.setName("Window 2");
        ticketThread2.start();
        System.out.println(ticketThread1.getClass()==ticketThread2.getClass());

    }
}

synchronized modifier block

Specify the lock object. After the thread obtains the lock of the object, other threads cannot obtain it. Note that this object should be unique at this time

Code implementation:

public class TicketThread extends Thread {
    static int number=10;
    static Object obj=new Object();
    @Override
    public void run() {
        while (true){
//Modify the code block, and the incoming object (obj) is unique
            synchronized (obj){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (number > 0) {
                    System.out.println(Thread.currentThread().getName()+"Got a ticket"+number);
                    number--;
                }
                else {
                    break;
                }
            }
}
}

synchronized modifier instance method

At this time, the default lock flag of synchronized is this. Note that if multiple threads access at the same time, it is allowed because the lock times of their thread objects are different. If shared resources are accessed at this time, thread safety cannot be guaranteed

public class TicketThread extends Thread {
    static int number=10;
    static Object obj=new Object();
    @Override
    public void run() {
        ticket();
    }
    /*
            Window 1 bought tickets 10
            Window 2 bought tickets 10
            Window 1 bought tickets 9
            Window 2 bought tickets 8
            Window 1 bought tickets 7
            Window 2 bought ticket 6
            Window 1 bought ticket 5
            Window 2 bought ticket 4
            Window 1 bought a ticket 3
            Window 2 bought ticket 2
            Window 1 bought ticket 1
    The reason for the above results is that when synchronized modifies the member method, the default object of the lock flag is this, and each thread will add its own lock,
    This lock is invalid for other threads. Therefore, when the first thread enters and the number -- operation has not been performed, the second thread enters

     */

    public synchronized  void ticket() {
        while(true){
            if(number>0){
                System.out.println(Thread.currentThread().getName()+"Got a ticket"+number);
                number--;
            }
            else break;
        }
    }
}

synchronized modified static method

Note that the difference from the decorated member method is that the synchronized lock flag means that the incoming object is a class object. At this time, even if multiple threads access this shared resource, it is thread safe; Because in the same class, the class objects of multiple threads are the same, that is, the lock is valid for all threads, system out. println(ticketThread1.getClass()==ticketThread2. getClass());// This sentence can be used to judge whether the class objects corresponding to two threads are the same;

Code implementation:

public class TicketThread extends Thread {
    static int number=10;
    static Object obj=new Object();
    @Override
    public void run() {
       
        ticket();
    }
    
   /*
    This will not happen when modifying a static method. The reason is that when modifying a static method, the object of the lock flag is the corresponding Class object by default, as long as the two thread objects are
    For objects of the same Class, the corresponding Class objects are the same, system out. println(ticketThread1.getClass()==ticketThread2. getClass());
    This sentence can be used to judge whether the Class objects corresponding to two threads are the same
    This lock will work on both threads. When one thread enters, the lock will be closed and the other thread will be blocked.
    */

    public synchronized  void ticket() {
        while(true){
            if(number>0){
                System.out.println(Thread.currentThread().getName()+"Got a ticket"+number);
                number--;
            }
            else break;
        }
    }
}

2.2 Lock (explicit)

Lock mainly realizes thread safety through the implementation class ReentrantLock of lock interface; Unlike the synchronized keyword, you need to manually close and release the lock. The locking method is lock(); The release method is unlock(); Note that when an exception occurs, the lock will not be released automatically. Therefore, generally speaking, the use of lock must be carried out in the try{}catch {} block, and the release of lock must be carried out in the finally block to ensure that all locks are released and prevent deadlock

The code implementation is:

import javax.swing.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentantLockDome extends Thread {
    static int number = 10;
    static Lock lock=new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();//Lock, so that the code quickly enters the locked state, and the other threads queue up
                Thread.sleep(50);
                if (number > 0) {
                    System.out.println(Thread.currentThread().getName() + "Got a ticket" + number);
                    number--;
                }else {
                    Thread.currentThread().stop();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            finally {
                lock.unlock();
            }
        }
    }

}

The test classes are:

import synchronzied.TicketThread;

public class Text {
    public static void main(String[] args) {
        ReentantLockDome r1=new ReentantLockDome();
        ReentantLockDome r2=new ReentantLockDome();
        r1.setName("Window 1");
        r2.setName("Window 2");
        r1.start();
        r2.start();
    }
}

Topics: Multithreading