Multithreading
Core:
- A program is an independent execution path
- When the program is running, even if you do not create a thread, there will be multiple threads in the background, such as the main thread and gc thread
- Main () is called the main thread, which is the entry of the system and is used to execute the whole program
- In a process, if multiple threads are opened up, the operation of threads is scheduled by the scheduler. The scheduler is closely related to the operating system, and the sequence can not be interfered by human beings
- When operating on the same resource, there will be a problem of resource grabbing, and concurrency control needs to be added
- Threads bring additional overhead, such as cpu scheduling time and concurrency control overhead
- Each thread interacts in its own working memory. Improper memory control will cause data inconsistency
1, Thread creation
1.1,Thread
- Custom Thread inherits Thread class
- Rewrite the run() method to write the thread execution body
- Create a thread object and call the start() method to start the thread
package study01; //Thread creation method 1: rewrite the run() method and call start to start the thread public class TestThread extends Thread{ @Override public void run() { //run method thread for (int i = 0; i < 20; i++) { System.out.println("I'm looking at the code---"+i); } } public static void main(String[] args) { //Main thread //Create a thread object TestThread testThread = new TestThread(); //Call start() to start the thread testThread.start(); //The result is that the output is inserted in the middle and executed alternately //testThread.run(); To run this is to first execute run (), a common method call for (int i = 0; i < 20; i++) { System.out.println("I'm learning multithreading--"+i); } } }
Note that thread startup is not necessarily executed immediately, but is scheduled by cpu
1.2. Thread realizes image download
The Commons IO package needs to be imported in advance
package study01; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; //Practice thread to download pictures through multiple threads public class TestThread2 extends Thread{ private String url;//Network picture address private String name;//Saved file name public TestThread2(String url, String name) { this.url = url; this.name = name; } //The execution body of the download image thread @Override//Override run method public void run() { WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url,name); System.out.println("The downloaded file is:"+name); } public static void main(String[] args) { TestThread2 csdn1 = new TestThread2("https://img-bss.csdnimg.cn/1627885834758.jpg","csdn1.jpg"); TestThread2 csdn2 = new TestThread2("https://img-home.csdnimg.cn/images/20210831092422.png","csdn2.png"); //Turn on Multithreading csdn1.start(); csdn2.start(); } } //Downloader class WebDownloader{ //Download method public void downloader(String url,String name){ try {//Use the package method FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("io abnormal"); } } }
1.3,Runnable
- Define the MyRunnable class to implement the Runnable interface
- Implement the run() method and write the thread execution body
- Create a thread object and call the start() method to start the thread
The Runnable object is recommended because of the limitations of Java single inheritance
package study01; public class TestThread3 implements Runnable{ //Create thread method: implement the runnable interface, rewrite the run method, and throw the execution thread into the implementation class of the runnable interface @Override public void run() { //run method thread for (int i = 0; i < 20; i++) { System.out.println("I'm looking at the code---"+i); } } public static void main(String[] args) { //Create an implementation class of the runnable interface TestThread3 testThread3 = new TestThread3(); //Create a thread object and start our thread agent through the thread object Thread thread = new Thread(testThread3); thread.start(); for (int i = 0; i < 20; i++) { System.out.println("I'm learning multithreading--"+i); } } }
Realize the problem of ticket grabbing
package study01; //Multiple threads operate on an object at the same time //Example of buying a train ticket public class TestThread4 implements Runnable{ //Number of votes private int ticketnums = 10; @Override public void run() { while (true){ if(ticketnums<=0){ break; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"-->Got it"+ticketnums--+"ticket"); } } public static void main(String[] args) { TestThread4 testThread4 = new TestThread4(); new Thread(testThread4,"Xiao Ming").start(); new Thread(testThread4,"teacher").start(); new Thread(testThread4,"cattle").start(); } }
But the result will be disorder and thread unsafe
Little exercise - tortoise and rabbit race
package study01; public class TestThread5 implements Runnable{ private static String winner; @Override public void run() { for (int i = 0; i <= 100; i++) { //Simulated rabbit rest if(Thread.currentThread().getName().equals("rabbit")&&i==50){ try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } if(Thread.currentThread().getName().equals("tortoise")&&i%5==0){ try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } //Judge whether the game is over //If the game is over, stop the program boolean flag = gameover(i); if(flag){ break; } System.out.println(Thread.currentThread().getName()+"Run away"+i+"step"); } } private boolean gameover(int steps){ //Judge whether there is a winner if(winner!=null){ return true; }{ if(steps>=100){ winner = Thread.currentThread().getName(); System.out.println("winner is "+winner); return true; } } return false; } public static void main(String[] args) { TestThread5 testThread5 = new TestThread5(); new Thread(testThread5,"rabbit").start(); new Thread(testThread5,"tortoise").start(); } }
1.4 Lambda expression
- Definition of functional interface:
- Any interface that contains only one abstract method is a functional interface
- For functional interfaces, we can create objects of the interface through lambda expressions
package study01; public class TestLambda1 { public static void main(String[] args) { //lambda simplification //Interface name = method (do not write method name, but write parameters) - > {implemented function} Ilike like = ()->{ System.out.println("i like lambda"); }; like.lambda(); } } interface Ilike{ void lambda(); }
If there is only one line, the curly braces can also be deleted, and the parameter type can also be removed
contrast:
package study01; public class TestLambda1 { //Static inner class static class Like implements Ilike{ @Override public void lambda() { System.out.println("i like lambda1"); } } public static void main(String[] args) { Ilike like = new Like(); like.lambda(); //Local inner class class Like2 implements Ilike{ @Override public void lambda() { System.out.println("i like lambda2"); } } like = new Like2(); like.lambda(); //Anonymous inner class, without class name, must rely on interface or parent class like = new Ilike() { @Override public void lambda() { System.out.println("i like lambda3"); } };//Semicolon is required like.lambda(); //lambda simplification like = ()->{ System.out.println("i like lambda4"); }; like.lambda(); } } interface Ilike{ void lambda(); }
The difference will be more obvious when you copy it into the compiler software
1.5 static agent
- Both real objects and proxy objects should implement the same interface
- The proxy object is to proxy the real role
package study01; public class StacticFrcxy { public static void main(String[] args) { //The following two are actually static agents. Runnable's implementation of multithreading is actually a static agent new Thread( ()-> System.out.println("I love you!")).start(); new WeddingCompany(new You()).HappyMarry(); } } interface Marry{ void HappyMarry(); } //Real role class You implements Marry{ @Override public void HappyMarry() { System.out.println("happy"); } } //delegable role class WeddingCompany implements Marry{ //The real goal of the agent private Marry target; public WeddingCompany(Marry target) { this.target = target; } @Override public void HappyMarry() { before(); this.target.HappyMarry(); //This is the real object after(); } private void after() { System.out.println("After marriage, the final payment"); } private void before(){ System.out.println("Before marriage, decorate the scene"); } }
Thread is equivalent to the wedding company as the proxy. The class You want to implement is equivalent to You (You) as the real role. Both of them are Runnable interfaces. Implementing the run method of thread is to implement the run method You rewrite. start multi threads to run the run method.
lambda expression is used in the middle to facilitate writing.
2, Thread state
1. Stop the thread
Here, the flag bit is used to stop the thread automatically (other methods are not recommended)
package study01; import java.util.Spliterator; //Stop thread //1. It is recommended that the thread stop normally -- > utilization times, and dead loop is not recommended //2. It is recommended to use flag bit -- > to set a flag bit //3. Do not use outdated methods such as stop or destroy or methods that are not recommended by jdk public class TestStop implements Runnable{ private boolean flag = true; @Override public void run() { int i = 0; while(flag){ System.out.println("run....thread"+i++); } } //Set a public stop 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){ //Call the stop method testStop.stop(); System.out.println("Thread stop"); } } } }
2. Thread hibernation
- sleep specifies the number of milliseconds the current thread is blocking
- Exception interruptedException in sleep
- When the sleep time arrives, the thread enters the ready state
- sleep can simulate network delay, countdown, etc
- Each object has a lock, and sleep does not release the lock
package study01;//Simulate network delay: amplify the occurrence of the problem public class TestSleep implements Runnable {/ / number of votes private int ticketnums = 10; @ override public void run() {while (true) {if (ticketnums < = 0) {break;} try {thread.sleep (200);} catch (interruptedexception E) {e.printstacktrace();} system.out.println (thread. Currentthread(). Getname() + "-- > got" + ticketnums -- > "ticket");}} public static void main (string [] args) {testthread4 testthread4 = new testthread4(); new thread (testthread4, "Xiao Ming"). Start(); new thread (testthread4, "teacher") . start(); new thread (testthread4, "scalpers"). Start();}}
//Analog countdown public class TestSleep2 { public static void main(String[] args) { TestSleep2 testSleep2 = new TestSleep2(); try { testSleep2.turndown(); } catch (InterruptedException e) { e.printStackTrace(); } } //Analog countdown public void tendown() throws InterruptedException { int num = 10; while(true){ Thread.sleep(1000); System.out.println(num--); if(num<0){ break; } } } }
3. Thread yield
- Comity thread, so that the currently executing thread stops pausing, but does not block
- Transition a thread from a running state to a ready state
- Let the CPU reschedule, comity is not necessarily successful! Look at CPU mood
package study01;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()+"Thread starts execution"); Thread.yield();//Comity to System.out.println(Thread.currentThread().getName() + "thread stops executing");}}
4. Thread enforcement (join)
package study01; //Test the join method. It is intended to jump the queue public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("thread vip Here we are"+i); } } public static void main(String[] args) throws InterruptedException { TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); //Main thread for (int i = 0; i < 1000; i++) { if(i==200){ thread.join(); } System.out.println("main"+i); } } }
5. Thread status
- See the JDK help document Thread.State for details
package study01; 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(); state = thread.getState(); System.out.println(state);//Run while(state!=Thread.State.TERMINATED){//Judge whether the thread will always output the state if it does not terminate thread.sleep(100); state = thread.getState(); //Update thread status System.out.println(state);//Output status } } }
After the thread enters the dead state, it cannot be started again
6. 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 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 get priority in the following ways
- getPriority()
- setPriority(int xxx)
package study01; import javax.sound.midi.Soundbank; //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()); } }
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 CPU scheduling
7. 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 operation logs in the background, monitor memory, garbage collection and wait
package study01; //Test daemon thread public class TestDaemon { public static void main(String[] args) { God god = new God(); Person person = new Person(); Thread thread = new Thread(god); thread.setDaemon(true); //The default is false, which means that it is a user thread. Normal threads are user threads... Here, change the god thread to a daemon thread thread.start(); new Thread(person).start(); //Start user thread } } //lord class God implements Runnable{ @Override public void run() { while (true){ System.out.println("God bless you"); } } } //you class Person implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("You live happily all your life"); } System.out.println("=====You're dead"); } }
gc thread is also a daemon thread, which cannot be seen intuitively
3, Thread synchronized
- In real life, we will encounter the problem that one resource is used by multiple people. The most natural solution is to queue up and come one by one
- When dealing with multithreading, when multiple threads access the same object and some threads want to modify the object, we need thread synchronization. Thread synchronization is actually a waiting mechanism. Multiple threads that need to access the object at the same time enter the * * object waiting pool * * to form a queue, wait for the previous thread to use it, and then use it again for the next thread
Forming condition: queue + lock
- 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 access 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:
- A thread holding a lock will cause all other threads that need the lock to hang;
- In multi thread 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, it will release the lock, which will lead to priority inversion and performance problems.
1. Unsafe cases (withdrawal)
package study02; //Two people went to the bank to withdraw money public class UnsafeBank { public static void main(String[] args) { //account Account account = new Account(100,"fund"); Drawing you = new Drawing(account,50,"you"); Drawing my = new Drawing(account,100,"I"); you.start(); my.start(); } } 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 //How much did you withdraw int drawingMoney; //How much money do you have now int nowmoney; public Drawing(Account account,int drawingMoney,String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } @Override public void run() { if(account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"The money is not enough"); return; } try { Thread.sleep(100); //Amplify the occurrence of the problem } catch (InterruptedException e) { e.printStackTrace(); } //Card balance = balance - you get money account.money = account.money-drawingMoney; //The money in your hand nowmoney = nowmoney + drawingMoney; System.out.println(account.name+"The balance is:"+account.money); System.out.println(this.getName()+"Now some money is:"+nowmoney); } } package study02; //Two people went to the bank to withdraw money public class UnsafeBank { public static void main(String[] args) { //account Account account = new Account(100,"fund"); Drawing you = new Drawing(account,50,"you"); Drawing my = new Drawing(account,100,"I"); you.start(); my.start(); } } 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 //How much did you withdraw int drawingMoney; //How much money do you have now int nowmoney; public Drawing(Account account,int drawingMoney,String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } @Override public void run() { if(account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"The money is not enough"); return; } try { Thread.sleep(100); //Amplify the occurrence of the problem } catch (InterruptedException e) { e.printStackTrace(); } //Card balance = balance - you get money account.money = account.money-drawingMoney; //The money in your hand nowmoney = nowmoney + drawingMoney; System.out.println(account.name+"The balance is:"+account.money); System.out.println(this.getName()+"Now some money is:"+nowmoney); } }
This often happens when two people withdraw money from an account at the same time, that is, when there are 1 million in the card, A wants to withdraw 500000, B wants to withdraw 1 million, and two people withdraw at the same time, they see 1 million and think they can withdraw, resulting in A card balance of - 50.
2. 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 uses: the synchronized method and the synchronized block.
- Synchronized methods control 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. The blocked thread can obtain the lock and continue to execute
- Defect: declaring a large method synchronized will affect efficiency
- The content that needs to be modified in the method needs to be locked. Too many locks waste resources
synchronized method:
package study02; //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,"Xiao Fang").start(); new Thread(buyTicket,"cattle").start(); } } class BuyTicket implements Runnable{ private int ticketnum=10; private boolean flag = true; @Override public void run() { while (flag){ buy(); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } //The synchronized synchronization method locks this private synchronized void buy(){ //Judge whether there are tickets if (ticketnum<=0){ flag = false; return; } //Buy a ticket System.out.println(Thread.currentThread().getName()+"Get"+ticketnum--); } }
The buy method is locked here. The default lock is this
Here, three people are lined up. Only after one person takes the ticket, the next person will continue to take the ticket to prevent negative numbers.
synchronized block:
synchronized(Obj){}
- Obj calls it a synchronization monitor
- Obj can be any object, but it is recommended to use shared resources as synchronization monitors
- There is no need to specify a synchronization monitor for synchronization in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself, or class [reflection details]
- Synchronization monitor execution
- The first thread accesses, locks the synchronization monitor, and executes the code in it
- The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed
- After the first thread is accessed, unlock the synchronization monitor
- The second thread accesses, finds that the synchronization monitor has no lock, and then locks and accesses
package study02; //Two people went to the bank to withdraw money public class UnsafeBank { public static void main(String[] args) { //account Account account = new Account(100,"fund"); Drawing you = new Drawing(account,50,"you"); Drawing my = new Drawing(account,100,"I"); you.start(); my.start(); } } 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 //How much did you withdraw int drawingMoney; //How much money do you have now int nowmoney; public Drawing(Account account,int drawingMoney,String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } //The synchronized method cannot be written here because it locks this by default. What we want to lock is the amount of change (addition, deletion, modification and query), but not here. @Override public void run() { synchronized (account){ //Judge whether there is money if(account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"The money is not enough"); return; } try { Thread.sleep(100); //Amplify the occurrence of the problem } catch (InterruptedException e) { e.printStackTrace(); } //Card balance = balance - you get money account.money = account.money-drawingMoney; //The money in your hand nowmoney = nowmoney + drawingMoney; System.out.println(account.name+"The balance is:"+account.money); System.out.println(this.getName()+"Now some money is:"+nowmoney); } } }
Here, the previous example solves the insecurity problem by adding synchronized blocks, and there will be no negative number problem. (observe the difference between the two)
3. Deadlock
- Multiple threads occupy some shared resources and wait for the resources occupied by other threads to run. As a result, two or more threads are waiting for each other to release resources and stop execution. When a synchronization block has "locks of more than two objects" at the same time, the problem of "deadlock" may occur.
package study02; //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,"Cinderella"); Makeup g2 = new Makeup(1,"Snow White"); g1.start(); g2.start(); } } //Lipstick class Lipstiok{} //mirror class Mirror{} class Makeup extends Thread{ //There is only one resource needed. Use static to ensure that there is only one static Lipstiok lipstiok = new Lipstiok(); static Mirror mirror = new Mirror(); int choice;//choice String girlName;//People who use cosmetics Makeup(int choice,String girlName){ this.choice=choice; this.girlName=girlName; } @Override public void run() { //Make up try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } //Makeup, holding each other's locks, is to get each other's resources private void makeup() throws InterruptedException { if (choice == 0) { synchronized (lipstiok) { //Get lipstick lock System.out.println(this.girlName + "Get lipstick lock"); Thread.sleep(1000); synchronized (mirror) {//Obtain the lock of the mirror after 1 second System.out.println(this.girlName + "Get the lock of the mirror"); } } } else { synchronized (mirror) { //Get the lock of the mirror System.out.println(this.girlName + "Get the lock of the mirror"); Thread.sleep(2000); synchronized (lipstiok) {//Get the lock of lipstick after 2 seconds System.out.println(this.girlName + "Get lipstick lock"); } } } } }
In this case, there will be a deadlock. Two people are deadlocked at the same time, resulting in the program jamming. If you want to change this situation, you only need not hold the other party's lock, that is, the second synchronized block of each conditional statement is placed outside the first synchronized block.
Avoidance method
-
Four necessary conditions for deadlock generation:
-
Mutex condition: a resource can only be used by one process at a time.
-
Request and hold condition: a process is blocked by requesting resources and keeps the obtained resources.
-
Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up.
-
Cyclic waiting condition: a cyclic waiting relationship is formed between several processes.
Just find a way to break one or more of them to avoid deadlock
-
4.lock lock
package study02; import java.util.concurrent.locks.ReentrantLock; public class Lock { public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(ticket).start(); new Thread(ticket).start(); new Thread(ticket).start(); } } class Ticket implements Runnable{ int tickernums = 10; //Define lock lock private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { lock.lock();//Lock if(tickernums>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(tickernums--); } else { break; } } finally { //Unlock lock.unlock(); } } } }
The problem can be solved by lock locking, which is more intuitive than synchronized blocks.
The performance of lock lock is higher, and lock lock is preferred.
4, Thread collaboration (producer consumer issues)
Realize the connection between producers and consumers.
1. Tube pass method
- Producer: module responsible for production data (may be method, object, thread, process);
- Consumer: the module responsible for processing data (possibly methods, objects, threads and processes);
- Buffer zone: consumers cannot directly use the producer's data. There is a "buffer zone" between them. The producer puts the produced data into the buffer zone, and the consumer takes out the data from the buffer zone