Thread safety problems and processing methods
Example: create three windows to sell tickets, with a total number of 100 tickets. Use the way to implement the Runnable interface
1. Problem: in the process of selling tickets, there are duplicate tickets and wrong tickets -- > there are thread safety problems
2. Cause of the problem: when a thread operates the ticket, and the operation has not been completed, other threads participate and operate the ticket.
3. How to solve it: when a thread a is operating a ticket, other threads cannot participate. No other thread can start to operate the ticket until thread a has finished operating the ticket. This situation cannot be changed even if thread a is blocked.
4. In Java, we solve the thread safety problem through synchronization mechanism.
- Method 1: synchronize code blocks
Synchronized (synchronized monitor){
//Code to be synchronized
}
explain:
1. The code that operates shared data is the code that needs to be synchronized. -- > it cannot contain more code or less code.
2. Shared data: variables operated by multiple threads. For example, ticket is shared data.
3. Synchronization monitor, commonly known as lock. Any class object can act as a lock.
Requirement: multiple threads must share the same lock.
4. Supplement: in the way of implementing Runnable interface to create multithreading, we can consider using this as the synchronization monitor. - Mode 2: synchronization method.
If the code that operates on shared data is completely declared in a method, we might as well synchronize this method declaration. - 5. The way of synchronization solves the safety problem of threads. - benefits
- When operating synchronous code, only one thread can participate and other threads wait. It is equivalent to a single threaded process, which is inefficient. - limitations
1. The synchronous code block handles the thread safety problem of implementing Runnable
class Window1 implements Runnable{ private int ticket = 100; // Object obj = new Object(); // Dog dog = new Dog(); @Override public void run() { // Object obj = new Object(); while(true){ synchronized (this){//this: the only object of Window1 / / method 2: synchronized (dog){ if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket); ticket--; } else { break; } } } } } public class WindowTest1 { public static void main(String[] args) { Window1 w = new Window1(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("Window 1"); t2.setName("Window 2"); t3.setName("Window 3"); t1.start(); t2.start(); t3.start(); } } class Dog{ }
2. Use synchronous code block to solve the Thread safety problem of inheriting Thread class
Example: create three windows to sell tickets, with a total of 100 votes. Use the method of inheriting Thread class
Note: in the way of inheriting Thread class to create multithreads, use this carefully as the synchronization monitor, and consider using the current class as the synchronization monitor.
class Window2 extends Thread{ private static int ticket = 100; private static Object obj = new Object(); @Override public void run() { while(true){ //correct // synchronized (obj){ synchronized (Window2.class){//Class clazz = Window2.class,Window2.class will be loaded only once //Wrong way: this represents T1, T2 and T3 objects // synchronized (this){ if(ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + ": Ticket No.:" + ticket); ticket--; }else{ break; } } } } } public class WindowTest2 { public static void main(String[] args) { Window2 t1 = new Window2(); Window2 t2 = new Window2(); Window2 t3 = new Window2(); t1.setName("Window 1"); t2.setName("Window 2"); t3.setName("Window 3"); t1.start(); t2.start(); t3.start(); } }
3. Use the synchronization method to solve the thread safety problem of implementing Runnable interface
class Window3 implements Runnable { private int ticket = 100; @Override public void run() { while (true) { show(); } } private synchronized void show(){//Sync monitor: this //synchronized (this){ if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket); ticket--; } //} } } public class WindowTest3 { public static void main(String[] args) { Window3 w = new Window3(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("Window 1"); t2.setName("Window 2"); t3.setName("Window 3"); t1.start(); t2.start(); t3.start(); } }
4. Use the synchronization method to deal with the Thread safety problem in the way of inheriting the Thread class
class Window4 extends Thread { private static int ticket = 100; @Override public void run() { while (true) { show(); } } private static synchronized void show(){//Synchronization monitor: Window4.class //private synchronized void show() {/ / synchronization monitor: t1,t2,t3. This solution is wrong if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": Ticket No.:" + ticket); ticket--; } } } public class WindowTest4 { public static void main(String[] args) { Window4 t1 = new Window4(); Window4 t2 = new Window4(); Window4 t3 = new Window4(); t1.setName("Window 1"); t2.setName("Window 2"); t3.setName("Window 3"); t1.start(); t2.start(); t3.start(); } }
Summary of synchronization methods:
- The synchronization method still involves the synchronization monitor, but we don't need to declare it explicitly.
- Non static synchronization method. The synchronization monitor is this
Static synchronization method. The synchronization monitor is the current class itself