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