Detailed explanation of multithreading
Learning route: thread introduction, thread implementation (key points), thread status, thread synchronization (key points), thread communication problems and advanced topics
The knowledge of thread implementation and thread synchronization should be mainly studied, and other parts are only required to be understood.
This note is the author's own summary from watching the video of station B for review and reference. If there is any infringement problem, please contact me to delete it
Original video station B portal: https://www.bilibili.com/video/BV1V4411p7EF?p=11&spm_id_from=333.1007.top_right_bar_window_history.content.click
01 thread introduction
Program: a program is an ordered collection of instructions and data. It has no running meaning and is a static concept.
Process: the process of executing a program. It is a dynamic concept. It is the unit of system resource allocation.
Usually, a process can include several threads. There is at least one thread in a process. Thread is the unit of CPU scheduling and execution.
True multithreading refers to having multiple CPU s, that is, multiple processors, such as servers. Single core processor can only simulate multithreading, that is, multiple tasks can be switched quickly. Because the switching is fast, there is the illusion of simultaneous execution.
1.1 core concepts of this chapter
- Threads are independent execution paths
- When the program is running, even if it does not create its own thread, there will be multiple threads in the background, such as main thread and gc thread (garbage collection thread provided by JVM);
- Main () is called the main thread, which is the entry of the program and is used to execute the whole program;
- The scheduler is closely related to the running sequence of multiple threads, which is closely related to the scheduler;
- When operating on the same resource, there will be the problem of resource grabbing, and concurrency control needs to be added;
- Threads will 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
02 thread creation
There are three ways to create threads:
[the external chain image transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-gxfjcbfv-1645195728053) (/ users / weiyifeng / library / Application Support / typora user images / image-20220213185227974. PNG)]
Mainly Thread class and Runnable class
2.1 usage of thread class:
- Custom Thread class inherits Thread class
- Rewrite the run() method to the portable thread executor
- Create a thread object and call the start() method to start the thread
//Create Thread method 1: inherit the Thread class, rewrite the run() method, and call start to start the Thread //Summary: Note: thread startup may not be executed immediately, but the CPU will schedule the execution public class TestThrad1 { public static class TestThread1 extends Thread{ @Override public void run() { for(int i=0 ;i<20 ;i++){ System.out.println("I'm looking at the code--"+i); } } } public static void main(String[] args) { TestThread1 testThread1 = new TestThread1(); testThread1.start(); for(int i=0;i<200;i++){ System.out.println("I'm writing code--"+i); } } }
Threads do not necessarily execute, but are scheduled by the cpu, and the execution results may be different each time.
//Java multithreading download online image instance import org.apache.commons.io.FileUtils; //Note that the operation of this program needs to import the external jar package common IO, right-click after selecting the jar package in idea, and click add to library to add it to the project import java.io.File; import java.io.IOException; import java.net.URL; public class TestThread2 extends Thread{ //Define two private variables: url and name. After that, they can only be initialized through the constructor and cannot be changed additionally private String url; private String name; //Override of run method @Override public void run() { WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url,name); System.out.println("Pictures downloaded successfully:"+name); } public TestThread2(String url, String name){ this.url=url; this.name=name; } public static void main(String[] args) { TestThread2 t1 = new TestThread2("https://img.wenku8.com/image/2/2537/2537s.jpg","1.jpg"); TestThread2 t2 = new TestThread2("https://img.wenku8.com/image/2/2521/2521s.jpg","2.jpg"); TestThread2 t3 = new TestThread2("https://img.wenku8.com/image/2/2730/2730s.jpg","3.jpg"); t1.start(); t2.start(); t3.start(); } } class WebDownloader{ public void downloader(String url,String name){ //In the exception handling part, a catch statement for capturing IO exceptions is added, and the error prompt information can be output after the exception is found try{ FileUtils.copyURLToFile(new URL(url),new File(name)); }catch(IOException e){ e.printStackTrace(); System.out.println("IO Abnormal, downloader There is a problem with the method!"); } } }
We speculate that the download order should be 1 jpg,2.jpg,3.jpg, but the actual operation results are shown in the figure below:
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-gl7wsncz-1645195728054) (/ users / weiyifeng / library / Application Support / typora user images / image-20220214205234099. PNG)]
Thus, we can conclude that our program is executed by multithreading, not according to the input order of 1, 2 and 3. Furthermore, it is verified that multithreading is not executed sequentially, but needs to be scheduled by CPU.
2.2 implementation of runnable interface
- 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
Runnable interface is a functional interface, and Lambda expressions can be used
Summary
- Inherit Thread class
-
- The subclass inherits the Thread class and has the ability of multithreading
- Start thread: subclass object start();
- Not recommended: avoid the limitation of OOP single inheritance
- Implement Runnable interface
-
- The implementation interface Runnable has multithreading capability
- Start Thread: pass in the target object + Thread object start()
- Recommended: it avoids the limitation of single inheritance, is flexible and convenient, and is convenient for the same object to be used by multiple threads
The following example is an introduction to the multithreading concurrency problem. This example reveals the thread insecurity in the case of multithreading operation.
//Multiple threads operate on an object at the same time //Example of buying a train ticket //Problems found: when multiple threads operate on the same resource, the thread is unsafe and the data is disordered //This problem will be explained in detail when it comes to multithreading concurrency public class TestThread4 implements Runnable { int ticketNum = 10; @Override public void run() { while(true) { if (ticketNum <= 0) { break; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Got the second" + ticketNum-- + "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(); } }
The screenshot of the program is shown in the following figure:
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-rlnfw26g-1645195728054) (/ users / weiyifeng / library / Application Support / typera user images / image-20220214213434325. PNG)]
2.3 implement Callable interface (just understand)
1. To implement the Callable interface, the return value type is required
2. When overriding the call method, you need to throw an exception
3. Create target object
4. Create execution service: executorservice ser = executors newFixedThreadPool(1);
5. Submit execution: future result1 = Ser subsmit(t1);
6. Get result: Boolean R1 = result1 get();
7. Shut down the service: Ser shutdownNow();
8. Advantages of Callable: 1. Return value can be defined; 2. Exceptions can be thrown
Code case:
public class TestCallable implements Callable<Boolean>{ //Return type needs to be added @Override public Boolean call(){ //The return type of the call function should be consistent with the return type in the above Callable brackets //Add the contents of the function to be executed return true; } public class void main(String[] args){ TestCallable t1 = new TestCallable(); TestCallable t2 = new TestCallable(); TestCallable t3 = new TestCallable(); //Create execution service ExecutorService ser = Executors.newFiexThreadPool(1); //Submit for execution Future<Boolean> r1 = ser.subsmit(t1); Future<Boolean> r2 = ser.subsmit(t2); Future<Boolean> r3 = ser.subsmit(t3); //Get results boolean rs1 = r1.get(); boolean rs2 = r2.get(); boolean rs3 = r3.get(); //Shut down service ser.shutdownNow(); } }
2.4 detailed explanation example of multithreading: Tortoise and rabbit race
/*Case: Tortoise and rabbit Race race Race race 1. First take a distance from the track, and then get closer and closer to the finish line 2. Judge whether the game is over 3. Print out the winner 4. The tortoise and rabbit race began 5. In the story, the tortoise wins and the rabbit needs to sleep, so we need to simulate the rabbit to sleep 6. Finally, the tortoise won the race * */ public class Race implements Runnable{ private static String winner; @Override public void run() { for(int i=0;i<=100;i++){ boolean flag = gameOver(i); if(flag == true){ break; } //Simulated rabbit rest if(Thread.currentThread().getName().equals("rabbit") && i%10 ==0){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"-->Run away"+i+"step"); } } private boolean gameOver(int steps){ 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) { Race race = new Race(); new Thread(race,"rabbit").start(); new Thread(race,"tortoise").start(); } }
2.5 static proxy mode
Summary of static proxy mode:
Both real objects and proxy objects should implement the same interface
The proxy object should represent the real role
Benefits:
Proxy objects can do many things that real objects cannot do
Real objects focus on doing their own things
Lambda expression instance of static proxy
new Thread(() -> System.out.println("I Love You")).start();
2.6 Lambda expression
03 thread status
Here, refer to the representation of thread switching state in the operating system course. This paper only briefly gives the representation of state switching
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-spxhbvj7-1645195728055) (/ users / weiyifeng / library / Application Support / typera user images / image-20220217164625702. PNG)]
method | explain |
---|---|
setPriority(int newPriority()) | Change the priority of a thread |
static void sleep(long mills) | 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.1 thread stop
- The stop() and destroy() methods provided by JDK are not recommended
- It is recommended to let 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
public class TestStop implements Runnable { //1. The ID used by the thread body is defined in the thread private boolean flag = true; @Override public void run(){ //2. The thread body uses this ID while(flag){ System.out.println("run... Thread"); } } //3. Identification of method change provided externally public void stop(){ this.flag = false; } }
3.2 thread hibernation_ sleep
- sleep specifies the number of milliseconds the current process is blocking
- There is an exception InterruptedException in sleep;
- When the sleep time arrives, the thread enters the ready state
- sleep can simulate network delay, countdown and so on
- Every object has a lock, and sleep will not release the lock
The function of sleep statement:
Role 1: simulate the role of network delay: amplify the occurrence of problems;
//Analog countdown public static void tenDown() throws IntertuptedException{ int num = 10; while(true){ Thread.sleep(1000); System.out.println(num--); if(num < 0){ break; } } }
Function 2: an important example: print the current system time
public static void main(String[] args){ Date startTime = new Data(System.currentTimeMillis()); //Get current system time while(true){ trY{ Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Data(System.currentTimeMillis()); }catch(InterruptException e){ e.printStackTrace(); } } }
3.3 comity_ yield
- 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
//Comity thread //Comity does not necessarily succeed. It depends on your mood public class TestTield{ } class MyYield implements Runnable{ @Override public void run(){ //Test comity thread //Comity does not necessarily succeed. It depends on your mood 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 executing"); Thread.yield(); //Thread comity System.out.println(Thread.currentThread().getName()+"The thread starts executing"); } }
3.4 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 //Imagine jumping in line public class TestJoin implements Runnable{ @Override public void run() { for(int i=0;i<100;i++){ System.out.println("thread vip coming!!!"+i); } } public static void main(String[] args) throws InterruptedException { TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for(int i=0;i<1000;i++){ if(i == 50){ thread.join(); } System.out.println("main" + i); } } }
3.5 thread state observation
Thread.State
Thread status. A thread can be in one of the following states:
- NEW
Threads that have not been started are in this state. - RUNNABLE
The thread executing in the Java 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.
In connection with the previous learning content, it should be noted that once the thread is interrupted or ends, it cannot be started again once it enters the dead 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 thread state of any operating system
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(); } } }); //Observation state Thread.State state = thread.getState(); System.out.println(state); //NEW //Observe the status after startup thread.start(); state = thread.getState(); System.out.println(state); //RUNNABLE Thread.sleep(1000); state = thread.getState(); //Update thread status System.out.println(state); } } }
3.6 thread priority
- Java provides a thread scheduler to detect 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 and obtain priorities in the following ways:
-
- getPriority()
- setPriority(int xxx)
3.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...
04 thread synchronization mechanism
-
Multiple threads operate on the same resource
-
Concurrency: the same object is operated by multiple threads at the same time
-
When dealing with multithreading, multiple threads access the same object, and some threads want to modify the object. At this time, we need thread synchronization. Thread synchronization is actually a waiting mechanism. Multiple threads that need to access this object at the same time enter the waiting pool of this object to form a queue, wait for the previous thread to use it, and then use it for the next thread.
-
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, The following problems exist:
-
- 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 to release the lock, it will lead to priority inversion and performance problems.
4.1 three unsafe cases
4.1.1 unsafe ticket purchase
//Unsafe ticket buying //Thread unsafe, negative number appears public class UnsafetyBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"a").start(); new Thread(station,"b").start(); new Thread(station,"c").start(); } } class BuyTicket implements Runnable{ private int num = 10; private boolean flag = true; @Override public void run() { while(flag){ buy(); } } //private synchronized void buy(){ // Adding synchronized method can solve some thread synchronization problems private void buy(){ //Judge whether there are tickets if(num <= 0){ flag = false; return; } //Simulation demonstration try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //Buy a ticket System.out.println(Thread.currentThread().getName()+" Get "+num--); } }
4.1.2 unsafe banks
//Unsafe withdrawal public class UnsafeBank { public static void main(String[] args) { //account Account account = new Account(100,"Marriage fund"); Drawing you = new Drawing(account,50,"you"); Drawing girlFriend = new Drawing(account,100,"Losers"); you.start(); girlFriend.start(); } } //account class Account{ int money; String name; public Account(int money, String name) { this.money = money; this.name = name; } } //Bank: simulated withdrawal class Drawing extends Thread{ Account account; //account //How much did you withdraw int drawMoney; //How much money do you have now int nowMoney; public Drawing(Account account, int drawMoney,String name){ super(name); this.account = account; this.drawMoney = drawMoney; } @Override public void run() { //synchronized(account){ // The object of the lock is the amount of change, which needs to be added, deleted and modified //Judge whether there is money if(account.money - drawMoney < 0){ System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw"); return; } //The problem of sleep amplification can occur try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } account.money = account.money - drawMoney; //The money in your hand nowMoney += drawMoney; System.out.println(account.name + " The balance is " + account.money); System.out.println(Thread.currentThread().getName()+"The money in hand is"+nowMoney); //} } }
4.1.3 thread unsafe collection
These three codes are too inky... I choose to ignore the third code directly. If you want to see it, you can go to the video website to find the code of this part, which is similar to that above
4.2 synchronization method and synchronization block
4.2.1 synchronization method
- Because we can use private keywords (get and set methods) 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: synchronized method and synchronized block
-
- Synchronization method: public synchronized void method(int args) {}
- Synchronized methods control access to "objects". Each pair 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. Otherwise, the blocked thread can obtain the lock and continue to execute
-
- Defect: if a large method is synchronized, it will affect efficiency
Note: only the contents that need to be modified in the method need to be locked. Too many locks will waste resources
The default synchronized method locks this, which is the class itself
4.2.2 synchronization block
- Synchronization 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 in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself, or class [explain in reflection]
- Synchronization monitor execution process
-
- 1. The first thread accesses, locks the synchronization monitor, and executes the code in it
- 2. The second thread accesses and finds 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, finds that the synchronization monitor has no lock, and then locks and accesses
Whether to use synchronized method or synchronized block mainly depends on which class is added, deleted, modified and queried
The synchronized method is equivalent to synchronized(this) {}
4.2.3 CopyOnWriteArrayList
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<String>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
4.2.4 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.
In short, deadlock is: multiple threads hold each other's required resources, and then form a deadlock
- 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 resources it has obtained
- Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived until the use is completed
- 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 find a way to break any one or more of them.
4.2.5 Lock
- From jdk5 Since 0, Java has provided a more powerful thread synchronization mechanism -- realizing synchronization by explicitly defining synchronization Lock objects. Synchronous locks use Lock objects as
- java.util.concurrent.locks.Lock is a tool that controls the access of multiple threads to shared resources. Locks provide 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
- The ReentrantLock class implements Lock. It has the same concurrency and memory semantics as synchronized. ReentrantLock is commonly used in thread safety control. It can explicitly add and release locks
class A{ private final ReentrantLock lock = new ReentrantLock(); public void m(){ lock.lock(); try{ lock.lock(); //Unsafe code ... }finally{ lock.unlock(); //If there are exceptions in the synchronization code, write unlock() to the finally statement block } } }
- synchronized vs Lock
-
- Lock is an explicit lock (manually open and close the lock, don't forget to close the lock), and synchronized is an implicit lock, which will be automatically released out of the scope
- Lock only has code 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:
-
- Lock - > synchronization code block (has entered the method body and allocated corresponding resources) - > synchronization method (outside the method body)
05 thread collaboration
5.1 producer consumer issues
This is a thread synchronization problem. Producers and consumers share the same resource, and producers and consumers depend on and condition each other.
- For producers, before producing products, they should inform consumers to wait, and after producing products, they need to inform consumers to consume immediately
- For consumers, after consumption, they should inform producers that they have finished consumption and need to produce new products for consumption.
- In the producer consumer problem, synchronized is not enough
-
- synchronized prevents concurrent updates from synchronizing a shared resource, enabling synchronization
- synchronized cannot be used for message passing (Communication) between different threads
Java provides several methods to solve the communication problem between threads:
Method name | effect |
---|---|
wait() | It means that the thread waits until other threads notify it. Unlike sleep, it will release the lock |
wait(long timeout) | Specifies the number of milliseconds to wait |
notify() | Wake up a waiting thread |
notifyAll() | Wake up all threads calling the wait method on the same object. Threads with high priority can be scheduled first |
Note: all methods of Object class can only be used in synchronization methods or synchronization code blocks, otherwise the exception IllegalMonitorStateException will be thrown
5.1.1 pipe pass method
Solution 1: introduce a third party buffer. The producer puts the produced data into the buffer, and the consumer takes out the data from the buffer
The code part should understand the meaning, and then be able to write it independently
package gaoji; //Testing: producer consumer model -- using buffer to solve: pipe process method //Producers, consumers, products, buffers public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); } } //producer class Productor extends Thread{ SynContainer container; public Productor(SynContainer container){ this.container = container; } //production @Override public void run() { for (int i = 0; i < 100; i++) { container.push(new Chicken(i)); System.out.println("Produced"+i+"Chicken"); } } } //consumer class Consumer extends Thread{ SynContainer container; public Consumer(SynContainer container){ this.container = container; } //Consumer consumption @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("Consumption --->" + container.pop().id + "Chicken"); } } } //product class Chicken{ int id; //Product number public Chicken(int id){ this.id = id; } } //buffer class SynContainer{ //A container size is required Chicken[] chickens = new Chicken[10]; //Container counter int count = 0; //Producers put in products public synchronized void push(Chicken chicken){ //If the container is full, it needs to wait for consumers to consume if(count == chickens.length){ //Inform consumers of consumption. Producer waiting try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //If the container is not full, we need to throw in the product chickens[count] = chicken; count ++; //Consumers can be informed of consumption. this.notifyAll(); } //Consumer products public synchronized Chicken pop(){ //Whether consumption can be judged if(count == 0){ //Wait for producers to produce and consumers to wait try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //If you can consume count--; Chicken chicken = chickens[count]; //After eating, inform consumers to produce this.notifyAll(); return chicken; } }
5.1.2 signal lamp method
Liberation method 2: traffic light method
package gaoji; //Test producer consumer problem 2: signal lamp method, flag bit solution public class TestPC2 { public static void main(String[] args) { TV tv = new TV(); new Player(tv).start(); new Watcher(tv).start(); } } //Producer -- > actor class Player extends Thread{ TV tv; public Player(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { if(i%2 == 0){ this.tv.play("Happy base camp"); }else{ this.tv.play("Shake the voice, tiktok the good life"); } } } } //Consumer -- > audience class Watcher extends Thread{ TV tv; public Watcher(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { this.tv.watch(); } } } //Products -- > Programs class TV{ //The actor performs and the audience waits for T //The audience watched and the actors waited for F String voice; boolean flag = true; //perform public synchronized void play(String voice){ if(this.flag == false){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("The actors performed:"+voice); //Inform the audience to watch this.notifyAll();; this.voice = voice; this.flag = !this.flag; } //Watch the program public synchronized void watch(){ if(flag == true){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("The audience watched:"+voice); //Inform the actors to perform this.notifyAll();; this.flag = !this.flag; } }
5.2 thread pool
- Background: resources that are often created and destroyed and used heavily, such as threads in concurrency, have a great impact on performance.
- Idea: create many threads in advance, put them into the thread pool, get them directly when using them, and put them back into the pool after using them. It can avoid frequent creation, destruction and reuse. Similar to public transport in life.
- Benefits:
-
- Improved response speed (reduced time to create new threads)
- Reduce resource consumption (reuse threads in the thread pool)
- Easy thread management
-
- corePoolSize: core pool size
- Maximum number of threads: poolsize
- keepAliveTime: when a thread has no task, how long does it last at most and then it will terminate