day23 Java thread safety

Posted by bigdspbandj on Sun, 24 Oct 2021 09:31:54 +0200

I know, i know
You accompany me on the other side of the earth




1, Thread safety

Suppose we need to simulate the ticket selling process of a cinema
The method of the Runnable interface is used here

/*
		There are 100 tickets
		It is divided into three windows
		Delay required before each print
*/
public class TicketRunnable1 implements Runnable {
    private static int ticket = 100;
    @Override
    public void run() {

        while(ticket > 0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(
                    Thread.currentThread().getName() + "Selling page"
                            + (ticket--)+"Ticket");
        }
    }
}
    TicketRunnable1 ticketRunnable1 = new TicketRunnable1();

    Thread thread1 = new Thread(ticketRunnable1);
    Thread thread2 = new Thread(ticketRunnable1);
    Thread thread3 = new Thread(ticketRunnable1);

    thread1.setName("Xiao Liu");
    thread2.setName("fgh");
    thread3.setName("Leek box ");

    thread1.start();
    thread2.start();
    thread3.start();
 There will be a large number of duplicate results in the operation results, even the 0th ticket
 This is thread safe

Judging whether a program has thread safety standards, none of the three is indispensable
1. Is there a multithreaded environment
2. Is there shared data / shared variables
3. Are there multiple statements operating on shared data / shared variables


2, How to solve thread safety

1. Using synchronous code blocks

Format:

	synchronized(object){
		Code that needs to be synchronized;
	}

The above problem exists after sleep(), which improves the possibility of outputting statements at the same time
Therefore, it can be modified to

public class synchronized1 implements Runnable{
    private static int i = 1;
    @Override
    public void run() {
        while(true){
        		// The code block requires an invariant object
            synchronized(this){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(i <= 100){
                    System.out.println(Thread.currentThread().getName()
                    + "Selling page" + i + "Ticket");
                i++;
                }
            }
        }
    }
}

Only one thread is allowed to enter the code block at a time. After one run, preemption is performed again
The essence is to degenerate the original multithreading into a single thread, reducing efficiency

matters needing attention

Wrong writing

Wrong writing

	//	Mode 1
	while (ticket <= 100) {
	    synchronized(this){
	        System.out.println(
	                Thread.currentThread().getName() 
	                + "Selling page" + (ticket--)+"Ticket");
    	}
    }
    //	Mode II
    synchronized (this) {
        while (ticket <= 100) {
            System.out.println(
                    Thread.currentThread().getName() 
                    + "Selling page" + (ticket--) + "Ticket");
        }
    }

This method will cause multiple threads to enter the loop, resulting in data overflow
Mode 2 will cause only one thread to enter the loop and run until the end of the loop

Unification of reference objects

  Normally, the lock object is any object
  Synchronization method: the lock object is this
  Synchronous static method:The lock object is the bytecode file object of the current class
public class Synchronized2 implements Runnable{

    private static int i = 1;
    @Override
    public void run() {

        while(true){
            if(i % 2 ==0){
                sell();
            }else{
                //  Since the sell() method is a static synchronization method, the objects here also need to be synchronized
                //  That is, Synchronized2.class. Otherwise, it is like two different locks
                synchronized(Synchronized2.class){
                    if(i <= 500){
                        System.out.println(Thread.currentThread().getName()+
                                "Selling page"+ i + "Ticket");
                    }
                    i++;
                }
            }
        }
    }
    //  The synchronized object here is the bytecode file object of the current class, that is, synchronized 2.class
    public synchronized static void sell(){
        if(i <= 500){
            System.out.println(Thread.currentThread().getName()
            + "Selling page"+ i + "Ticket");
        }
        i++;
    }
}

2. Using Lock object Lock

A new Lock object Lock is provided after JDK 1.5

  Lock:(Interface)
    Specific subclasses: ReentrantLock
            void lock() Acquire lock
            void unlock() Release lock
public class Lock1 implements Runnable{
    private static int i = 1;
	//	Create subclass objects
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            lock.lock();
            if(i <= 100){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()
                        + "Selling page"+ i + "Ticket");
                i++;
            }
            lock.unlock();
        }
    }
}

The effect is similar to synchronizing code blocks


3, wait for wakeup – notify

Producer, consumer issues
In practice, it has always been that the producer completes the production and then the consumer purchases it
Introduced into code logic
It can be understood as generating or updating data first, and then calling it by code
This requires a tacit understanding, or order, between multiple threads
In this case, the wake-up waiting mechanism can be used

Here, we simulate by generating and updating object members and then reading them
Custom class

class Pokemon {
    private String name;
    private  int level;
    boolean flag;

    public Pokemon() {
    }
    get & set
    ...
}

Set member

public class SetThread1 implements Runnable {

    Pokemon s;
    private int x = 0;

    SetThread1(Pokemon s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                if (x <= 200) {
                    if (s.flag) {
                        //  The data is fresh and does not need to be updated
                        try {
                            s.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        s.notify();

                    } else {
                        //  The data is out of date and needs to be updated
                        if (x % 2 == 0) {
                            s.setName("Eevee");
                            s.setLevel(15);
                            x++;
                        } else {
                            s.setName("Pikachu");
                            s.setLevel(20);
                            x++;
                        }
                        //  Notify get thread
                        s.setFlag(true);
                        s.notify();
                    }
                }
            }
        }
    }
}

Read member

public class GetThread1 extends Thread {
    Pokemon s;
    GetThread1(Pokemon s) {
        this.s = s;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                if (s.flag) {
                    //  The data is fresh and needs to be obtained
                    System.out.println(s.getName() + "---" + s.getLevel());
                    s.setFlag(false);
                    s.notify();
                } else {
                    //  When the data expires, notify the set thread to update
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    s.notify();
                }
            }
        }
    }
}

Build thread

public class PokemonTest {

    public static void main(String[] args) {

        Pokemon pokemon = new Pokemon();

        SetThread1 setThread1 = new SetThread1(pokemon);
        GetThread1 getThread1 = new GetThread1(pokemon);

        Thread thread1 = new Thread(setThread1);
        Thread thread2 = new Thread(getThread1);

        thread1.start();
        thread2.start();
    }
}
Output results: Eevee---15
         Pikachu---20
		 Eevee---15
		 Pikachu---20
		 Eevee---15
		 ...

summary

1. Three conditions of thread safety may occur (need to exist at the same time)

    1,Is there a multithreaded environment
    2,Is there shared data/Shared variable
    3,Are there multiple statements that operate on shared data/Shared variable

2. Solution to thread synchronization security
Scheme I:
Sync code block:
Format: synchronized (lock object){
Code that needs to be synchronized (code blocks that operate on shared data / shared variables)
}

        Use of lock objects:
            Note: the lock objects of multi threads with synchronization safety should be consistent and unique
        Normally, the lock object is any object
        Synchronization method: the lock object is this
        Synchronous static method:The lock object is the bytecode file object of the current class

Scheme II:
Lock lock, appears after JDK1.5
Lock
unlock() releases the lock

3,wait() & notify() & notifyAll()
wait()
The thread enters a wait blocking state

notify()
Randomly wake up a thread waiting for blocking state and enter synchronous blocking state

notifyAll()
Wake up all threads waiting for blocking state and enter synchronous blocking state