Detailed explanation of JAVA multithreading (II)
III Thread state
1. Five states
2. Thread method
method | explain |
---|---|
setPriority(int newPriority) | Change the priority of a thread |
static void sleep(long millis) | Hibernates the currently executing thread for the specified number of milliseconds |
void join() | Wait for the thread to terminate |
static void yield() | Pauses the currently executing thread object and executes other threads |
void interrupt() | Interrupt the thread. Don't do it this way |
boolean isAlive() | Test whether the thread is active |
3. Thread stop
It is not recommended to use the stop() destroy() method provided by JDK [obsolete].
It is recommended that the thread stop by itself
It is recommended to use a flag bit to terminate the variable. When flag= false, the thread will be terminated.
//Test stop //1. It is recommended that the thread should be stopped normally and the number of utilization times should be increased. Dead loop is not recommended //2. It is recommended to use the flag bit and set the flag bit //3. Do not use outdated methods such as stop or destroy, or methods not recommended by JDK public class TestStop implements Runnable{ //1. Set a flag bit private boolean flag=true; @Override public void run(){ int i=0; while (flag){ System.out.println("run..Thread"+i++); } } //2. Set a stop flag of the thread and 2. Set a stop flag of the method public void stop(){ this.flag=false; } public static void main(String[] args) { TestStop testStop = new TestStop(); new Thread(testStop).start(); for (int i = 0; i < 1000; i++) { System.out.println("main.."+i); if(i==900){ //Switch the flag bit to stop the thread testStop.stop(); System.out.println("The thread stopped"); } } } } ---------------Result segment--------------- ... run..Thread1129 run..Thread1130 main..900 run..Thread1131 The thread stopped main..901 main..902 main..903 ...
4. Thread hibernation
Sleep specifies the number of milliseconds the current thread is blocking
Exception in sleep, InterruptedException;
After the sleep time reaches, the thread enters the ready state;
sleep can simulate network delay, countdown, etc
Every object has a lock, and sleep will not release the lock
Demo: timing:
//Analog countdown public class TestSleep { public static void main(String[] args) { try { tenDown(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void tenDown() throws InterruptedException { int num=10; while (true){ Thread.sleep(1000); System.out.println(num--); if (num<=0){ break; } } } } ---------------result--------------- 10 9 8 7 6 5 4 3 2 1
import java.text.SimpleDateFormat; import java.util.Date; //Analog countdown public class TestSleep { public static void main(String[] args) { Date startTime = new Date(System.currentTimeMillis());//Get the current time of the system while (true){ try { System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); Thread.sleep(1000); startTime = new Date(System.currentTimeMillis());//Update current time } catch (InterruptedException e) { e.printStackTrace(); } } } } ---------------result--------------- Always output the current computer time 09:40:34 09:40:35 09:40:36 09:40:37
5. Comity
Comity thread: pause the currently executing thread without blocking it
Change the thread from running state to ready state
Let the CPU reschedule, comity is not necessarily successful! Look at CPU mood
//Test comity thread public class TestYield { public static void main(String[] args) { MyYield myYield = new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"The thread starts running"); Thread.yield();//Comity System.out.println(Thread.currentThread().getName()+"Thread stop execution"); } } ---------------result--------------- a The thread starts running b The thread starts running a Thread stop execution b Thread stop execution
6. Thread enforcement - Join
Join merge threads. After this thread completes execution, execute other threads. Other threads are blocked
Imagine jumping in line
//Test Join method public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("thread vip..."+i); } } public static void main(String[] args) throws InterruptedException { //Start our thread TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); //Main thread for (int i = 0; i < 30; i++) { if (i==20){ thread.join();//Jump in line } System.out.println("main..."+i); } } } ---------------result--------------- main...0 main...1 ... main...19 thread vip...0 ... thread vip...19 main...20 ... main...29
7. Thread state observation
Thread. State
Thread state. A thread can be in one of the following states.
NEW Threads that have not been started are in this state. RUNNABLE stay Java The thread executing in the virtual machine is in this state. BLOCKED Threads that are blocked waiting for a monitor lock are in this state. WAITING A thread that is waiting for another thread to perform a specific action is in this state. TIMED WAITING The thread that is waiting for another thread to perform the action for the specified waiting time is in this state. TERMINATED The exited thread is in this state.
A thread can be in a state at a given point in time. These states are virtual machine states that do not reflect the state of any operating system thread.
//Observe the status of the test thread public class TestState { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("///"); }); //Observation state Thread.State state = thread.getState(); System.out.println(state);//new //Observe after startup thread.start();//Start thread state = thread.getState(); System.out.println(state);//run while (state != Thread.State.TERMINATED){ //As long as the thread does not terminate, it will always output the state Thread.sleep(700); state = thread.getState(); //Update thread status System.out.println(state);//Output status } } } ---------------result--------------- NEW RUNNABLE TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING /// TERMINATED
8. Thread priority
Java provides a thread scheduler to monitor all threads that enter the ready state after startup. The thread scheduler determines which thread should be scheduled to execute according to priority.
The priority of threads is expressed in numbers, ranging from 1 to 10
Thread.MIN_PRIORITY= 1;
Thread.MAX_PRIORITY=10;
Thread.NORM_PRIORITY= 5;
Change or obtain priorities in the following ways
getPriority(). setPriority(int xxx)
Priority setting is recommended before start() scheduling
Low priority only means that the probability of obtaining scheduling is low, not that it will not be called if the priority is low, which depends on the scheduling of CPU
//Test thread priority public class TestPriority{ public static void main(String[] args) { //Default priority of main thread System.out.println(Thread.currentThread().getName()+"-->" +Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread t1 = new Thread(myPriority); Thread t2 = new Thread(myPriority); Thread t3 = new Thread(myPriority); Thread t4 = new Thread(myPriority); //Set priority before starting t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(Thread.MAX_PRIORITY); t4.start(); } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->" +Thread.currentThread().getPriority()); } } ---------------result--------------- main-->5 Thread-3-->10 Thread-0-->5 Thread-1-->1 Thread-2-->4
Daemon thread
Threads are divided into user threads and daemon threads
The virtual machine must ensure that the user thread has completed execution
The virtual machine does not have to wait for the daemon thread to finish executing
For example, record the operation log in the background, monitor the memory, and wait for garbage collection
//Test daemon thread public class TestDaemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true);//The default is false, which means user thread thread.start();//Daemon thread start new Thread(you).start();//User thread start } } class God implements Runnable{ @Override public void run() { while (true){ System.out.println("God is always alive"); } } } class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("--You're alive--"); } System.out.println("goodbye world"); } } ---------------result--------------- When the user thread ends, the virtual machine ends and the daemon thread stops
IV Thread synchronization (key)
Multiple threads operate on the same resource
1. Concurrency
Concurrency: the same object is operated by multiple threads at the same time
2. Thread synchronization
In real life, we will encounter the problem that "many people want to use the same resource". For example, everyone wants to eat in the canteen. The most natural solution is to queue up One by one
When dealing with multithreading, multiple threads access the same object, and some threads also want to modify the object
At this time, we need thread synchronization Thread synchronization is actually a waiting mechanism. Multiple threads need to be accessed at the same time
The thread of this object enters the waiting pool of this object to form a queue, waiting for the previous thread to use up, and then the next thread can use it again
3. Queues and locks
Forming conditions: queue + lock. We need these two things to solve the problem of thread insecurity
4. Thread synchronization
Because multiple threads of the same process share the same storage space, it brings convenience and access conflict. In order to ensure the correctness of data being accessed in the method, the lock mechanism synchronized is added during access. When one thread obtains the exclusive lock of the object and monopolizes resources, other threads must wait and release the lock after use
There are the following problems
- Holding a lock by one thread will cause all other threads that need the lock to hang
- In multi-threaded competition, locking and releasing locks will lead to more context switching and scheduling delays, resulting in performance problems
- If a high priority thread waits for a low priority thread to release the lock, the priority will be reversed
Set, causing performance problems
5. Three unsafe cases
Case 1:
//Unsafe ticket buying public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"Xiao Ming").start(); new Thread(buyTicket,"I").start(); new Thread(buyTicket,"cattle").start(); } } class BuyTicket implements Runnable{ private int ticketNums=10; //Number of votes boolean flag=true; //External stop mode @Override public void run() { //Buy a ticket while (flag){ buy(); } } private void buy(){ //Judge whether there are tickets if (ticketNums<0){ flag=false; return; } //Analog delay try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"Got it:"+ticketNums-- ); } } ---------------result--------------- Xiao Ming got it:10 Cattle got it:9 I got it:9 Xiao Ming got it:8 Cattle got it:7 I got it:6 Xiao Ming got it:5 I got it:4 Cattle got it:3 Xiao Ming got it:2 I got it:1 Cattle got it:0 Xiao Ming got it:-1
Case 2:
//Unsafe withdrawal public class UnsageBank { public static void main(String[] args) { Account account = new Account(10000, "Wedding bride price"); Drawing you = new Drawing(account, 5000, "Xiao Ming"); Drawing girlFriend = new Drawing(account, 10000, "girl friend"); you.start(); girlFriend.start(); } } //account class Account{ int money;//balance String name;//Card name public Account(int money, String name) { this.money = money; this.name = name; } } class Drawing extends Thread{ Account account;//account int drawingMoney;//How much did you withdraw int nowMoney;//How much money do you have public Drawing(Account account,int drawingMoney,String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } //Withdrawal operation @Override public void run() { //Judge whether there is money if (account.money-drawingMoney<0){ System.out.println("Insufficient account balance!"); return; } //Simulation delay can reduce the occurrence of method problems try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //Card balance = balance - get money account.money=account.money-drawingMoney; //The money in your hand nowMoney=nowMoney+drawingMoney; System.out.println(account.name+"Card balance:"+account.money); //nowMoney+Thread.currentThread().getName() == this.getName() System.out.println(this.getName()+"Money in hand:"+nowMoney); } } ---------------result--------------- Balance in wedding gift card:-5000 Balance in wedding gift card:-5000 Money in hand: Xiaoming 5000 Money in girlfriend's hand: 10000
Case 3:
import java.util.ArrayList; //Thread unsafe collection public class UnsafeList { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } } ---------------result--------------- 9997
6. Synchronization method and synchronization block
- Synchronization method
Because we can use the private keyword to ensure that data objects can only be accessed by methods, we only need to propose a mechanism for methods. This mechanism is the synchronized keyword, which includes two usages: the synchronized method and the synchronized block
Synchronization method: public synchronized void method (int args) {}
The synchronized method controls the access to "objects". Each object corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before it can be executed, otherwise the thread will block. Once the method is executed, it will monopolize the lock until the method returns, and the lock will not be released until the blocked thread can obtain the lock and continue to execute
Defect: declaring a large method synchronized will affect efficiency
Only the contents that need to be modified in the method need to be locked. Too many locks waste resources
- Synchronization block
Synchronization block: synchronized(obj) {}
obj calls it a synchronization monitor
obj can be any object, but shared resources are recommended as synchronization monitors
There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself, or class [explained in reflection]
Synchronization monitor execution process
1. The first thread accesses, locks the synchronization monitor and executes the code therein
2. When the second thread accesses, it is found that the synchronization monitor is locked and cannot be accessed
3. After the first thread is accessed, unlock the synchronization monitor
4. The second thread accesses and finds that the synchronization monitor has no lock, and then locks and accesses it
7. Case modification
After modification of case 1:
//Buy tickets safely public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"Xiao Ming").start(); new Thread(buyTicket,"I").start(); new Thread(buyTicket,"cattle").start(); } } class BuyTicket implements Runnable{ private int ticketNums=10; //Number of votes boolean flag=true; //External stop mode @Override public void run() { //Buy a ticket while (flag){ //Analog delay try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } buy(); } } //synchronized synchronization method private synchronized void buy(){ //Judge whether there are tickets if (ticketNums<=0){ flag=false; return; } System.out.println(Thread.currentThread().getName()+"Got it:"+ticketNums-- ); } } ---------------result--------------- Xiao Ming got it:10 Cattle got it:9 I got it:8 I got it:7 Cattle got it:6 Xiao Ming got it:5 Xiao Ming got it:4 Cattle got it:3 I got it:2 I got it:1
Case 2 after modification:
//Safe withdrawal public class UnsageBank { public static void main(String[] args) { Account account = new Account(10000, "Wedding bride price"); Drawing you = new Drawing(account, 5000, "Xiao Ming"); Drawing girlFriend = new Drawing(account, 10000, "girl friend"); you.start(); girlFriend.start(); } } //account class Account{ int money;//balance String name;//Card name public Account(int money, String name) { this.money = money; this.name = name; } } class Drawing extends Thread{ Account account;//account int drawingMoney;//How much did you withdraw int nowMoney;//How much money do you have public Drawing(Account account,int drawingMoney,String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } //Withdrawal operation @Override public void run() { //Locked shared resources synchronized (account){ //Judge whether there is money if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"Insufficient balance found!"); return; } //Simulation delay can reduce the occurrence of method problems try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //Card balance = balance - get money account.money=account.money-drawingMoney; //The money in your hand nowMoney=nowMoney+drawingMoney; System.out.println(account.name+"Card balance:"+account.money); //nowMoney+Thread.currentThread().getName() == this.getName() System.out.println(this.getName()+"Money in hand:"+nowMoney); } } } ---------------result--------------- Balance in wedding gift card: 5000 Money in hand: Xiaoming 5000 Girlfriend found insufficient balance!
After modification of case 3:
import java.util.ArrayList; //Thread is not a very safe collection //If sleep is removed, the main thread will output before other threads finish executing //You can judge later and end the main thread after other threads are finished public class UnsafeList { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } } ---------------result--------------- 10000
Expansion:
import java.util.concurrent.CopyOnWriteArrayList; //Test the collection of JUC security types public class TestJUC { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } } ---------------result--------------- 10000
8. Deadlock
Multiple threads occupy some shared resources and wait for the resources occupied by other threads to run
This causes two or more threads to stop executing while waiting for each other to release resources A synchronization block
When you have "locks of more than two objects" at the same time, the problem of "deadlock" may occur
Deadlock cases:
//Deadlock: multiple threads hold each other's required resources, and then form a deadlock public class DeadLock { public static void main(String[] args) { Makeup g1 = new Makeup(0, "Xiao Hong"); Makeup g2 = new Makeup(1, "Blue "); g1.start(); g2.start(); } } class Dior{ } class Mirror{ } class Makeup extends Thread{ //There is only one resource needed. Use static to ensure that there is only one static Dior dior= new Dior(); static Mirror mirror = new Mirror(); int choice;//choice String girlName;//User Makeup(int choice,String girlName){ this.choice=choice; this.girlName=girlName; } @Override public void run() { //Make up try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } //Make up, hold each other's locks, and need to get each other's resources private void makeup() throws InterruptedException { if (choice==0){ synchronized (dior){//Get lipstick lock System.out.println(this.girlName+"Get lipstick lock"); Thread.sleep(1000); //Want to get it in a second, mirror synchronized (mirror){ System.out.println(this.girlName+"Get the lock of the mirror"); } } }else { synchronized (mirror){//Get lipstick lock System.out.println(this.girlName+"Get the lock of the mirror"); Thread.sleep(2000); //Want to get lipstick in a second synchronized (dior){ System.out.println(this.girlName+"Get lipstick lock"); } } } } } ---------------result--------------- Little red gets the lock of lipstick Xiaolan gets the lock of the mirror
It never ends
9. Deadlock avoidance method
Four necessary conditions for deadlock generation
- Mutually exclusive condition: a resource can only be used by one process at a time.
- Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources.
- Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up.
- Circular waiting condition: a circular waiting resource relationship is formed between several processes.
The four necessary conditions for deadlock are listed above. We can avoid deadlock as long as we try to break any one or more of them
Case modification:
public class DeadLock { public static void main(String[] args) { Makeup g1 = new Makeup(0, "Xiao Hong"); Makeup g2 = new Makeup(1, "Blue "); g1.start(); g2.start(); } } class Dior{ } class Mirror{ } class Makeup extends Thread{ //There is only one resource needed. Use static to ensure that there is only one static Dior dior= new Dior(); static Mirror mirror = new Mirror(); int choice;//choice String girlName;//User Makeup(int choice,String girlName){ this.choice=choice; this.girlName=girlName; } @Override public void run() { //Make up try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } //Make up, hold each other's locks, and need to get each other's resources private void makeup() throws InterruptedException { if (choice==0){ synchronized (dior){//Get lipstick lock System.out.println(this.girlName+"Get lipstick lock"); Thread.sleep(1000); } //Want to get it in a second, mirror synchronized (mirror){ System.out.println(this.girlName+"Get the lock of the mirror"); } }else { synchronized (mirror){//Get lipstick lock System.out.println(this.girlName+"Get the lock of the mirror"); Thread.sleep(2000); } //Want to get lipstick in a second synchronized (dior){ System.out.println(this.girlName+"Get lipstick lock"); } } } } ---------------result--------------- Little red gets the lock of lipstick Xiaolan gets the lock of the mirror Xiaolan gets the lock of lipstick Xiao Hong gets the lock of the mirror
10. Lock
Starting from JDK5, Java provides a more powerful thread synchronization mechanism -- synchronization is achieved by explicitly defining synchronization Lock objects. Synchronous locks use Lock objects as
java.util.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared resources. Lock provides exclusive access to shared resources. Only one thread can lock the lock object at a time. Threads should obtain the lock object before accessing shared resources
ReentrantLock (reentrant Lock) class implements Lock. It has the same concurrency and memory semantics as synchronized. ReentrantLock is commonly used in thread safety control, which can explicitly add and release locks.
example:
import java.util.concurrent.locks.ReentrantLock; //Test lock lock public class TestLock { public static void main(String[] args) { TestLock2 testLock2 = new TestLock2(); new Thread(testLock2,"Xiao Ming").start(); new Thread(testLock2,"Xiao Hong").start(); new Thread(testLock2,"I").start(); } } class TestLock2 implements Runnable{ int ticketNums=10; //Define lock lock private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } try { lock.lock();//Lock if (ticketNums>0){ System.out.println(Thread.currentThread().getName()+"Got it:"+ticketNums--); }else { break; } }finally { lock.unlock();//Unlock } } } } ---------------result--------------- I got it:10 Xiao Ming got it:9 Xiao Hong got it:8 I got it:7 Xiao Ming got it:6 Xiao Hong got it:5 Xiao Hong got it:4 I got it:3 Xiao Ming got it:2 Xiao Hong got it:1
11. Comparison between synchronized and Lock
Lock is an explicit lock (manually open and close the lock, don't forget to close the lock). synchronized is an implicit lock, which is automatically released out of the scope
Lock only has code block lock, and synchronized has code block lock and method lock
Using Lock lock, the JVM will spend less time scheduling threads and perform better. And it has better scalability (providing more subclasses)
Priority order
- Lock > synchronization code block (it has entered the method body and allocated corresponding resources) > synchronization method (on the side)
(outside the legal body)