1. Implement multithreading
1.1 understanding of multithreading
It refers to the technology that multiple threads execute concurrently from software or hardware. Computers with multithreading capability can execute multiple threads at the same time due to hardware support, so as to improve performance.
1.2 concurrency and parallelism
Parallelism: multiple instructions are executed simultaneously on multiple CPU s at the same time.
Concurrency: at the same time, multiple instructions are executed alternately on a single CPU.
1.3 processes and threads
Process: is a running program
Independence: a process is not only a basic unit that can run independently, but also an independent unit that the system allocates resources and schedules. Dynamics: the essence of a process is an execution process of a program. A process is generated and dies dynamically
Thread: it is a single sequential control flow in a process and an execution path
Single thread: if a process has only one execution path, it is called a single threaded program
Multithreading: if a process has multiple execution paths, it is called a multithreaded program
1.4 implement multi process 1: inherit Thread class
Method name | explain |
---|---|
void run() | After the thread is started, this method will be called and executed |
void start() | When this thread starts executing, the Java virtual opportunity calls the run method () |
In the defined method, override the run method
public class MyThread extends Thread { @Override public void run() { for(int i=0; i<100; i++) { System.out.println(i); } } } public class MyThreadDemo { public static void main(String[] args) { MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); // my1.run(); // my2.run(); //void start() causes this thread to start execution; The Java virtual machine calls the run method of this thread my1.start(); my2.start(); } }
-
Why override the run() method?
Because run() is used to encapsulate the code executed by the thread
-
What is the difference between the run() method and the start() method?
run(): encapsulates the code executed by the thread. It is called directly, which is equivalent to the call of ordinary methods
start(): start the thread; The run() method of this thread is then called by the JVM
1.5 implementation of multi process 2: implementation of Runnable interface
Method name | explain |
---|---|
Thread(Runnable target) | Assign a new Thread object |
Thread(Runnable target, String name) | Assign a new Thread object |
After defining the MyRunnable class, implement the Runnable interface, override the run() method in the method, create the Thread class object, and take the MyRunnable object as the parameter of the construction method
public class MyRunnable implements Runnable { @Override public void run() { for(int i=0; i<100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } public class MyRunnableDemo { public static void main(String[] args) { //Create an object of the MyRunnable class MyRunnable my = new MyRunnable(); //Create an object of Thread class and take the MyRunnable object as the parameter of the construction method //Thread(Runnable target) // Thread t1 = new Thread(my); // Thread t2 = new Thread(my); //Thread(Runnable target, String name) Thread t1 = new Thread(my,"Tank"); Thread t2 = new Thread(my,"aircraft"); //Start thread t1.start(); t2.start(); } }
1.6 implementation of multithreading 3: implementation of Callable interface
Method name | explain |
---|---|
V call() | Calculate the result. If the result cannot be calculated, an exception will be thrown |
FutureTask(Callable<V> callable) | Create a FutureTask and execute the given Callable once it is run |
V get() | If necessary, wait for the calculation to complete, and then obtain its results |
Implementation steps
-
Define a class MyCallable to implement the Callable interface
-
Override the call() method in the MyCallable class
-
Create an object of the MyCallable class
-
Create the FutureTask object of the implementation class of Future, and take the MyCallable object as the parameter of the construction method
-
Create an object of Thread class and take FutureTask object as the parameter of construction method
-
Start thread
-
Then call the get method to get the result after the thread ends
public class MyCallable implements Callable<String> { @Override public String call() throws Exception { for (int i = 0; i < 100; i++) { System.out.println("Confess to the girl" + i); } //The return value represents the result after the thread runs return "promise"; } } public class Demo { public static void main(String[] args) throws ExecutionException, InterruptedException { //After the thread is started, you need to execute the call method inside MyCallable mc = new MyCallable(); //Thread t1 = new Thread(mc); //You can get the results after the Thread is executed It can also be passed as a parameter to the Thread object FutureTask<String> ft = new FutureTask<>(mc); //Create thread object Thread t1 = new Thread(ft); String s = ft.get(); //Open thread t1.start(); //String s = ft.get(); System.out.println(s); } }
Comparison of three implementation methods
-
Implement Runnable and Callable interfaces
-
Advantages: strong extensibility. You can inherit other classes while implementing this interface
-
Disadvantages: the programming is relatively complex and the methods in the Thread class cannot be used directly
-
-
Inherit Thread class
-
Benefits: programming is relatively simple. You can directly use the methods in the Thread class
-
Disadvantages: poor scalability, unable to inherit other class 1.7 settings and obtain thread names
-
Method name | explain |
---|---|
void setName(String name) | Change the name of this thread to equal the parameter name |
String getName() | Returns the name of this thread |
Thread currentThread() | Returns a reference to the thread object currently executing |
public class MyThread extends Thread { public MyThread() {} public MyThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); } } } public class MyThreadDemo { public static void main(String[] args) { MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); //void setName(String name): change the name of this thread to be equal to the parameter name my1.setName("high-speed rail"); my2.setName("aircraft"); //Thread(String name) MyThread my1 = new MyThread("high-speed rail"); MyThread my2 = new MyThread("aircraft"); my1.start(); my2.start(); //static Thread currentThread() returns a reference to the thread object currently executing System.out.println(Thread.currentThread().getName()); } }
1.7 thread hibernation
Method name | explain |
---|---|
static void sleep(long millis) | Causes the currently executing thread to stay (pause execution) for the specified number of milliseconds |
public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "---" + i); } } } public class Demo { public static void main(String[] args) throws InterruptedException { /*System.out.println("Before going to bed "); Thread.sleep(3000); System.out.println("Wake up ");*/ MyRunnable mr = new MyRunnable(); Thread t1 = new Thread(mr); Thread t2 = new Thread(mr); t1.start(); t2.start(); } }
1.8 thread priority
Thread scheduling
-
Two scheduling modes
-
Time sharing scheduling model: all threads use the right to use the CPU in turn, and evenly allocate the time slice of CPU occupied by each thread
-
Preemptive scheduling model: give priority to the threads with high priority to use CPU. If the threads have the same priority, one will be selected randomly. The threads with high priority will obtain more CPU time slices
-
-
Java uses a preemptive scheduling model
-
Randomness
If the computer has only one CPU, then the CPU can only execute one instruction at a certain time. Only when the thread gets the CPU time slice, that is, the right to use, can it execute the instruction. Therefore, the execution of multithreaded programs is random, because it is not certain who grabs the right to use the CPU
Priority related methods
Method name | explain |
---|---|
final int getPriority() | Returns the priority of this thread |
final void setPriority(int newPriority) | Change the priority of this thread. The default priority of this thread is 5; Thread priority ranges from 1 to 10 |
public class MyCallable implements Callable<String> { @Override public String call() throws Exception { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "---" + i); } return "Thread execution is complete"; } } public class Demo { public static void main(String[] args) { //Priority: 1 - 10 default: 5 MyCallable mc = new MyCallable(); FutureTask<String> ft = new FutureTask<>(mc); Thread t1 = new Thread(ft); t1.setName("aircraft"); t1.setPriority(10); //System.out.println(t1.getPriority());//5 t1.start(); MyCallable mc2 = new MyCallable(); FutureTask<String> ft2 = new FutureTask<>(mc2); Thread t2 = new Thread(ft2); t2.setName("Tank"); t2.setPriority(1); //System.out.println(t2.getPriority());//5 t2.start(); } }
1.9 daemon thread
Method name | explain |
---|---|
void setDaemon(boolean on) | Mark this thread as a daemon thread. When all running threads are daemon threads, the Java virtual machine will exit |
public class MyThread1 extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName() + "---" + i); } } } public class MyThread2 extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + "---" + i); } } } public class Demo { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); MyThread2 t2 = new MyThread2(); t1.setName("goddess"); t2.setName("spare tire"); //Set the second thread as the daemon thread //When the normal thread is executed, there is no need for the daemon thread to continue running t2.setDaemon(true); t1.start(); t2.start(); } }
2. Thread synchronization
2.1 ticket selling
-
Case requirements
A cinema is currently showing domestic blockbusters, with a total of 100 tickets, and it has three windows for ticket sales. Please design a program to simulate the ticket sales of the cinema
-
Implementation steps
-
Define a class SellTicket to implement the Runnable interface, which defines a member variable: private int tickets = 100;
-
Rewrite the run() method in SellTicket class to realize ticket selling. The code steps are as follows
-
If the number of votes is greater than 0, sell the tickets and tell which window it is sold
-
After selling the tickets, the total number of votes will be reduced by 1
-
The ticket is sold out and the thread stops
-
Define a test class SellTicketDemo with the main method. The code steps are as follows
-
Create an object of SellTicket class
-
Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name
-
Start thread
-
public class SellTicket implements Runnable { private int tickets = 100; //Rewrite the run() method in SellTicket class to realize ticket selling. The code steps are as follows @Override public void run() { while (true) { if(ticket <= 0){ //out of stock break; }else{ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ticket--; System.out.println(Thread.currentThread().getName() + "Selling tickets,Remaining" + ticket + "Ticket"); } } } } public class SellTicketDemo { public static void main(String[] args) { //Create an object of SellTicket class SellTicket st = new SellTicket(); //Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name Thread t1 = new Thread(st,"Window 1"); Thread t2 = new Thread(st,"Window 2"); Thread t3 = new Thread(st,"Window 3"); //Start thread t1.start(); t2.start(); t3.start(); } }
2.2 problems in ticket sales
-
There is a problem selling tickets
-
The same ticket appeared many times
-
There were negative votes
-
-
Causes of problems
Due to the randomness of thread execution, the execution right of cpu may be lost during ticket selling, resulting in problems
2.3 synchronizing code blocks to solve data security problems
-
Conditions for safety problems
-
Is a multithreaded environment
-
Shared data
-
Multiple statements operate on shared data
-
-
How to solve the problem of multithreading security?
-
Basic idea: let the program have no security environment
-
-
How?
-
Lock the code for multiple statements to operate the shared data, so that only one thread can execute at any time
-
Java provides a way to synchronize code blocks
-
-
Sync code block format:
Synchronized (any object){ Code for multiple statements to operate on shared data }
Synchronized (arbitrary object): it is equivalent to locking the code, and any object can be regarded as a lock
-
Advantages and disadvantages of synchronization
-
Benefits: it solves the data security problem of multithreading
-
Disadvantages: when there are many threads, each thread will judge the lock on synchronization, which is very resource-consuming and will virtually reduce the running efficiency of the program
-
public class SellTicket implements Runnable { private int tickets = 100; private Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { // Lock the code that may have security problems. Multiple threads must use the same lock //When t1 comes in, it will lock this code if (tickets > 0) { try { Thread.sleep(100); //t1 rest 100ms } catch (InterruptedException e) { e.printStackTrace(); } //Window 1 is selling ticket 100 System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket"); tickets--; //tickets = 99; } } //When t1 comes out, the lock of this code is released } } } public class SellTicketDemo { public static void main(String[] args) { SellTicket st = new SellTicket(); Thread t1 = new Thread(st, "Window 1"); Thread t2 = new Thread(st, "Window 2"); Thread t3 = new Thread(st, "Window 3"); t1.start(); t2.start(); t3.start(); } }
2.4 synchronization method to solve data security problems
-
Format of synchronization method
Synchronization method: add the synchronized keyword to the method
-
Modifier synchronized return value type method name (method parameter){ Method body; }
What is the lock object of the synchronization method?
this
-
Static synchronization method
Synchronous static method: add the synchronized keyword to the static method
Modifier static synchronized return value type method name (method parameter){ Method body; }
What is the lock object for synchronous static methods?
Class name class
public class MyRunnable implements Runnable { private static int ticketCount = 100; @Override public void run() { while(true){ if("Window one".equals(Thread.currentThread().getName())){ //Synchronization method boolean result = synchronizedMthod(); if(result){ break; } } if("Window II".equals(Thread.currentThread().getName())){ //Synchronous code block synchronized (MyRunnable.class){ if(ticketCount == 0){ break; }else{ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } ticketCount--; System.out.println(Thread.currentThread().getName() + "Selling tickets,Remaining" + ticketCount + "Ticket"); } } } } } private static synchronized boolean synchronizedMthod() { if(ticketCount == 0){ return true; }else{ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } ticketCount--; System.out.println(Thread.currentThread().getName() + "Selling tickets,Remaining" + ticketCount + "Ticket"); return false; } } } public class Demo { public static void main(String[] args) { MyRunnable mr = new MyRunnable(); Thread t1 = new Thread(mr); Thread t2 = new Thread(mr); t1.setName("Window one"); t2.setName("Window II"); t1.start(); t2.start(); } }
2.5Lock lock
Although we can understand the Lock object problem of synchronization code blocks and synchronization methods, we do not directly see where the Lock is added and where the Lock is released. In order to more clearly express how to add and release the Lock, JDK5 provides a new Lock object Lock
Lock is an interface that cannot be instantiated directly. Its implementation class ReentrantLock is used to instantiate it here
Method name | explain |
---|---|
ReentrantLock() | Create an instance of ReentrantLock |
Method name | explain |
---|---|
void lock() | Acquire lock |
void unlock() | Release lock |
public class Ticket implements Runnable { //Number of tickets private int ticket = 100; private Object obj = new Object(); private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { //synchronized (obj) {/ / multiple threads must use the same lock try { lock.lock(); if (ticket <= 0) { //out of stock break; } else { Thread.sleep(100); ticket--; System.out.println(Thread.currentThread().getName() + "Selling tickets,Remaining" + ticket + "Ticket"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } // } } } } public class Demo { public static void main(String[] args) { Ticket ticket = new Ticket(); Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); Thread t3 = new Thread(ticket); t1.setName("Window one"); t2.setName("Window II"); t3.setName("Window three"); t1.start(); t2.start(); t3.start(); } }
2.6 deadlock
Thread deadlock refers to that two or more threads hold the resources required by each other, resulting in these threads being in a waiting state and unable to execute
public class Demo { public static void main(String[] args) { Object objA = new Object(); Object objB = new Object(); new Thread(()->{ while(true){ synchronized (objA){ //Thread one synchronized (objB){ System.out.println("Well off students are walking"); } } } }).start(); new Thread(()->{ while(true){ synchronized (objB){ //Thread two synchronized (objA){ System.out.println("Xiao Wei is walking"); } } } }).start(); } }
3. Producers and consumers
3.1 mode overview
Producer consumer model is a very classic multi-threaded cooperation model. Understanding the producer consumer problem can make us have a deeper understanding of multi-threaded programming.
The so-called producer consumer problem actually includes two types of threads:
One is the producer thread used to produce data
One is consumer thread, which is used to consume data
In order to decouple the relationship between producers and consumers, shared data areas are usually used, just like a warehouse
The producer's production data is directly placed in the shared data area, and does not need to care about the behavior of consumers
Consumers only need to obtain data from the shared data area, and do not need to care about the behavior of producers
Method name | explain |
---|---|
void wait() | Causes the current thread to wait until another thread calls the notify() method or notifyAll() method of the object |
void notify() | Wake up a single thread waiting for the object monitor |
void notifyAll() | Wake up all threads waiting for the object monitor |
3.2 cases
Case requirements
-
Table class (Desk): define variables representing the number of steamed stuffed buns, lock object variables, and variables marking whether there are steamed stuffed buns on the table
-
Producer class (Cooker): implement the Runnable interface, rewrite the run() method, and set the thread task
1. Judge whether there are packages and decide whether the current thread executes
2. If there are steamed stuffed buns, enter the waiting state. If there are no steamed stuffed buns, continue to execute and produce steamed stuffed buns
3. After producing steamed stuffed buns, update the status of steamed stuffed buns on the table to awaken consumers to consume steamed stuffed buns
-
Consumer class (Foodie): implement the Runnable interface, rewrite the run() method, and set the thread task
1. Judge whether there are packages and decide whether the current thread executes
2. If there are no steamed stuffed buns, enter the waiting state. If there are steamed stuffed buns, consume steamed stuffed buns
3. After consuming steamed stuffed buns, update the status of steamed stuffed buns on the table to wake up the producers to produce steamed stuffed buns
-
Test class (Demo): there is a main method. The code steps in the main method are as follows
Create producer and consumer thread objects
Open two threads respectively
public class Desk { //Define a tag //true means that there are hamburgers on the table. At this time, it is allowed to eat goods //false means that there are no hamburgers on the table. At this time, the chef is allowed to execute public static boolean flag = false; //Total number of hamburgers public static int count = 10; //Lock object public static final Object lock = new Object(); } public class Cooker extends Thread { // Producer steps: // 1. Judge whether there are hamburgers on the table // If there is, wait. If not, it will be produced. // 2. Put the hamburger on the table. // 3. Wake up the waiting consumers and start eating. @Override public void run() { while(true){ synchronized (Desk.lock){ if(Desk.count == 0){ break; }else{ if(!Desk.flag){ //production System.out.println("The cook is making hamburgers"); Desk.flag = true; Desk.lock.notifyAll(); }else{ try { Desk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } public class Foodie extends Thread { @Override public void run() { // 1. Judge whether there are hamburgers on the table. // 2. If not, wait. // 3. Eat if you have one // 4. After eating, there are no hamburgers on the table // Wake up the waiting producers and continue production // The total number of hamburgers is reduced by one //tricks: //1. while(true) loop //2. For synchronized locks, the lock object should be unique //3. Judge whether the shared data is over end //4. Judge whether the shared data is over Not over while(true){ synchronized (Desk.lock){ if(Desk.count == 0){ break; }else{ if(Desk.flag){ //have System.out.println("The food is eating hamburgers"); Desk.flag = false; Desk.lock.notifyAll(); Desk.count--; }else{ //No, just wait //What object is used as a lock, you must use this object to call wait and wake methods try { Desk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } public class Demo { public static void main(String[] args) { /*Consumer steps: 1,Judge whether there are hamburgers on the table. 2,If not, wait. 3,If you have it, eat it 4,After eating, there are no hamburgers on the table Wake up the waiting producers and continue production The total number of hamburgers is reduced by one*/ /*Producer steps: 1,Judge whether there are hamburgers on the table If there is, wait. If not, it will be produced. 2,Put the hamburger on the table. 3,Wake up the waiting consumers and start eating.*/ Foodie f = new Foodie(); Cooker c = new Cooker(); f.start(); c.start(); } }
3.3 optimization
-
The variables in the Desk class are encapsulated in an object-oriented manner
-
The constructor in the producer and consumer classes receives the Desk class object and then uses it in the run method
-
Create producer and consumer thread objects, and pass in Desk class objects in the constructor
-
Open two threads
public class Desk { //Define a tag //true means that there are hamburgers on the table. At this time, it is allowed to eat goods //false means that there are no hamburgers on the table. At this time, the chef is allowed to execute //public static boolean flag = false; private boolean flag; //Total number of hamburgers //public static int count = 10; //In the future, we will use this variable that must have a default value // private int count = 10; private int count; //Lock object //public static final Object lock = new Object(); private final Object lock = new Object(); public Desk() { this(false,10); // Call the parameter inside the empty parameter to assign a value to the member variable, and then you can use the member variable directly } public Desk(boolean flag, int count) { this.flag = flag; this.count = count; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public Object getLock() { return lock; } @Override public String toString() { return "Desk{" + "flag=" + flag + ", count=" + count + ", lock=" + lock + '}'; } } public class Cooker extends Thread { private Desk desk; public Cooker(Desk desk) { this.desk = desk; } // Producer steps: // 1. Judge whether there are hamburgers on the table // If there is, wait. If not, it will be produced. // 2. Put the hamburger on the table. // 3. Wake up the waiting consumers and start eating. @Override public void run() { while(true){ synchronized (desk.getLock()){ if(desk.getCount() == 0){ break; }else{ //System.out.println("verify whether it is executed"); if(!desk.isFlag()){ //production System.out.println("The cook is making hamburgers"); desk.setFlag(true); desk.getLock().notifyAll(); }else{ try { desk.getLock().wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } public class Foodie extends Thread { private Desk desk; public Foodie(Desk desk) { this.desk = desk; } @Override public void run() { // 1. Judge whether there are hamburgers on the table. // 2. If not, wait. // 3. Eat if you have one // 4. After eating, there are no hamburgers on the table // Wake up the waiting producers and continue production // The total number of hamburgers is reduced by one //tricks: //1. while(true) loop //2. For synchronized locks, the lock object should be unique //3. Judge whether the shared data is over end //4. Judge whether the shared data is over Not over while(true){ synchronized (desk.getLock()){ if(desk.getCount() == 0){ break; }else{ //System.out.println("verify whether it is executed"); if(desk.isFlag()){ //have System.out.println("The food is eating hamburgers"); desk.setFlag(false); desk.getLock().notifyAll(); desk.setCount(desk.getCount() - 1); }else{ //No, just wait //What object is used as a lock, you must use this object to call wait and wake methods try { desk.getLock().wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } public class Demo { public static void main(String[] args) { /*Consumer steps: 1,Judge whether there are hamburgers on the table. 2,If not, wait. 3,If you have it, eat it 4,After eating, there are no hamburgers on the table Wake up the waiting producers and continue production The total number of hamburgers is reduced by one*/ /*Producer steps: 1,Judge whether there are hamburgers on the table If there is, wait. If not, it will be produced. 2,Put the hamburger on the table. 3,Wake up the waiting consumers and start eating.*/ Desk desk = new Desk(); Foodie f = new Foodie(desk); Cooker c = new Cooker(desk); f.start(); c.start(); } }
3.4 basic use of blocked queue
-
Common BlockingQueue:
ArrayBlockingQueue: the bottom layer is an array, bounded
LinkedBlockingQueue: the bottom layer is a linked list, unbounded But it's not really unbounded. The maximum is the maximum value of int
-
The core method of BlockingQueue:
put(anObject): put the parameters into the queue. If they are not put in, they will be blocked
take(): fetch the first data, otherwise it will be blocked
public class Demo02 { public static void main(String[] args) throws Exception { // Object to create a blocking queue with a capacity of 1 ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1); // Storage element arrayBlockingQueue.put("hamburger"); // Take element System.out.println(arrayBlockingQueue.take()); System.out.println(arrayBlockingQueue.take()); // If you can't get it, it will block System.out.println("The program is over"); } }
3.5 blocking queue to realize waiting wake-up mechanism
-
Producer class (Cooker): implement the Runnable interface, rewrite the run() method, and set the thread task
1. Receive a blocking queue object in the construction method
2. In the run method, loop to add packets to the blocking queue
3. Print and add results
-
Consumer class (Foodie): implement the Runnable interface, rewrite the run() method, and set the thread task
1. Receive a blocking queue object in the construction method
2. Loop through the run method to get the packets in the blocking queue
3. Print the obtained results
-
Test class (Demo): there is a main method. The code steps in the main method are as follows
Create blocking queue object
Create producer thread and consumer thread objects, and construct the incoming blocking queue object in the method
Open two threads respectively
public class Cooker extends Thread { private ArrayBlockingQueue<String> bd; public Cooker(ArrayBlockingQueue<String> bd) { this.bd = bd; } // Producer steps: // 1. Judge whether there are hamburgers on the table // If there is, wait. If not, it will be produced. // 2. Put the hamburger on the table. // 3. Wake up the waiting consumers and start eating. @Override public void run() { while (true) { try { bd.put("hamburger"); System.out.println("The cook put in a hamburger"); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Foodie extends Thread { private ArrayBlockingQueue<String> bd; public Foodie(ArrayBlockingQueue<String> bd) { this.bd = bd; } @Override public void run() { // 1. Judge whether there are hamburgers on the table. // 2. If not, wait. // 3. Eat if you have one // 4. After eating, there are no hamburgers on the table // Wake up the waiting producers and continue production // The total number of hamburgers is reduced by one //tricks: //1. while(true) loop //2. For synchronized locks, the lock object should be unique //3. Judge whether the shared data is over end //4. Judge whether the shared data is over Not over while (true) { try { String take = bd.take(); System.out.println("Eat goods will" + take + "Take it out and eat it"); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Demo { public static void main(String[] args) { ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1); Foodie f = new Foodie(bd); Cooker c = new Cooker(bd); f.start(); c.start(); } }
4. Thread pool
4.1 introduction
Thread state | Specific meaning |
---|---|
NEW | The state of a thread that has not been started. It is also called initial state and start state. The thread was just created but not started. The start method has not been called. MyThread t = new MyThread() has only thread images and no thread characteristics. |
RUNNABLE | When we call the start method of the thread object, the thread object enters the RUNNABLE state. At this time, a thread is really created in the JVM process. Once the thread is started, it is not executed immediately. Whether the thread runs or not depends on the command and CPU scheduling. Then we call this intermediate state RUNNABLE, that is, it is qualified for execution, but it is not really executed, but waiting for the degree of CPU. |
BLOCKED | When a thread attempts to obtain an object lock and the object lock is held by other threads, the thread enters the Blocked state; When the thread holds a lock, the thread becomes Runnable. |
WAITING | The state of a waiting thread. It is also called waiting state. There are two reasons for thread waiting: calling object wait() and join() methods. A thread in a waiting state is waiting for another thread to perform a specific operation. For example, a thread waiting for wait () is waiting for another thread to call notify() or notifyAll(); A thread waiting for join () is waiting for another thread to end. |
TIMED_WAITING | The state of a thread waiting for a limited time. It is also called time limited waiting state. There are three reasons for the thread time limited wait state: thread sleep(long),Object.wait(long),join(long). |
TERMINATED | The state of a fully running thread. It is also called termination state and end state |
4.2 basic principles
The cost of creating a thread in the system is relatively high, because it involves interaction with the operating system. When a large number of threads with very short lifetime need to be created in the program, the resource consumption of the system caused by frequent thread creation and destruction may be greater than that of business processing
To control the consumption of resources, it is a bit "discarding the basics and discarding the end". In this case, in order to improve performance, we can use thread pool. When the thread pool is started, a large number of idle threads will be created. When we submit a task to the thread pool, the thread pool will start a thread to execute the task. After the task is executed, the thread does not die, but returns to the thread pool again, which is called idle state. Wait for the execution of the next task.
Design idea of thread pool:
-
Prepare a task container
-
Start multiple (2) consumer threads at once
-
At first, the task container was empty, so all threads were wait ing
-
Until an external thread throws a "task" into the task container, a consumer thread will be awakened
-
The consumer thread takes out the "task" and executes the task
-
After completion, continue to wait for the arrival of the next task
4.3 thread pool Executors default thread pool
We can use the static methods provided in Executors to create thread pools
static ExecutorService newCachedThreadPool() creates a default thread pool} static newFixedThreadPool(int nThreads) creates a thread pool that specifies the maximum number of threads
package com.mythreadpool; //static ExecutorService newCachedThreadPool() creates a default thread pool //static newFixedThreadPool(int nThreads) Create a thread pool that specifies the maximum number of threads import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MyThreadPoolDemo { public static void main(String[] args) throws InterruptedException { //1. Create a default thread pool object The pool is empty by default By default, it can accommodate up to the maximum value of int type ExecutorService executorService = Executors.newCachedThreadPool(); //Executors --- can help us create thread pool objects //ExecutorService --- can help us control the thread pool executorService.submit(()->{ System.out.println(Thread.currentThread().getName() + "In execution"); }); //Thread.sleep(2000); executorService.submit(()->{ System.out.println(Thread.currentThread().getName() + "In execution"); }); executorService.shutdown(); } }
4.4 on line process pool designation
static ExecutorService newFixedThreadPool(int nThreads): creates a thread pool with a specified maximum number of threads
package com.mythreadpool; //static ExecutorService newFixedThreadPool(int nThreads) //Create a thread pool that specifies the maximum number of threads import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class MyThreadPoolDemo2 { public static void main(String[] args) { //The parameter is not the initial value, but the maximum value ExecutorService executorService = Executors.newFixedThreadPool(10); ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService; System.out.println(pool.getPoolSize());//0 executorService.submit(()->{ System.out.println(Thread.currentThread().getName() + "In execution"); }); executorService.submit(()->{ System.out.println(Thread.currentThread().getName() + "In execution"); }); System.out.println(pool.getPoolSize());//2 // executorService.shutdown(); } }
4.5ThreadPoolExecutor
ThreadPoolExecutor ThreadPoolExecutor = new ThreadPoolExecutor (number of core threads, maximum number of threads, maximum lifetime of idle threads, task queue, creation of thread factory, rejection policy of task);
package com.mythreadpool; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class MyThreadPoolDemo3 { // Parameter 1: number of core threads // Parameter 2: maximum number of threads // Parameter 3: maximum idle thread lifetime // Parameter 4: time unit // Parameter 5: task queue // Parameter 6: create thread factory // Parameter 7: task rejection policy public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.shutdown(); } }
4.6 thread pool parameters
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) corePoolSize: The maximum value of core thread cannot be less than 0 maximumPoolSize: The maximum number of threads cannot be less than or equal to 0, maximumPoolSize >= corePoolSize keepAliveTime: Maximum idle thread lifetime,Cannot be less than 0 unit: Time unit workQueue: Task queue, cannot be empty null threadFactory: Create thread factory,Cannot be null handler: Task rejection policy,Cannot be null
4.7 non default task rejection policy
ThreadPoolExecutor.AbortPolicy: discards the task and throws RejectedExecutionException. Is the default policy.
ThreadPoolExecutor.DiscardPolicy: discard the task without throwing an exception, which is not recommended.
ThreadPoolExecutor. Discard oldest policy: discards the longest waiting task in the queue, and then adds the current task to the queue.
ThreadPoolExecutor.CallerRunsPolicy: call the run() method of the task to bypass the thread pool and execute directly.
Note: specify the number of tasks that can be executed by the thread pool = queue capacity + maximum number of threads
5. Atomicity
5.1 volatile issues
package com.myvolatile; public class Demo { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); t1.setName("Path classmate"); t1.start(); MyThread2 t2 = new MyThread2(); t2.setName("Xiao pi"); t2.start(); } } package com.myvolatile; public class Money { public static int money = 100000; } package com.myvolatile; public class MyThread1 extends Thread { @Override public void run() { while(Money.money == 100000){ } System.out.println("The marriage fund is no longer 100000"); } } package com.myvolatile; public class MyThread2 extends Thread { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } Money.money = 90000; } }
Procedural problem: Although the girl knows that the marriage fund is 100000, when the balance of the fund changes, the girl cannot know the latest balance.
5.2 volatile solution
When thread A modifies the shared data, thread B does not get the latest value in time. If the original value is still used, there will be A problem
1. Heap memory is unique. Each thread has its own thread stack.
2. When each thread uses variables in the heap, it will first copy a copy to the variable copy.
3. In the thread, each use is obtained from the copy of the variable.
Volatile keyword: force the thread to look at the latest value of the shared area every time it is used
package com.myvolatile; public class Demo { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); t1.setName("Path classmate"); t1.start(); MyThread2 t2 = new MyThread2(); t2.setName("Xiao pi"); t2.start(); } } package com.myvolatile; public class Money { public static volatile int money = 100000; } package com.myvolatile; public class MyThread1 extends Thread { @Override public void run() { while(Money.money == 100000){ } System.out.println("The marriage fund is no longer 100000"); } } package com.myvolatile; public class MyThread2 extends Thread { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } Money.money = 90000; } }
5.3synchronized
1. The thread obtains the lock
2. Empty the variable copy
3. Copy the latest value of the shared variable to the variable copy
4. Execute code
5. Assign the value in the modified variable copy to the shared data
6. Release the lock
package com.myvolatile2; public class Demo { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); t1.setName("Path classmate"); t1.start(); MyThread2 t2 = new MyThread2(); t2.setName("Xiao pi"); t2.start(); } } package com.myvolatile2; public class Money { public static Object lock = new Object(); public static volatile int money = 100000; } package com.myvolatile2; public class MyThread1 extends Thread { @Override public void run() { while(true){ synchronized (Money.lock){ if(Money.money != 100000){ System.out.println("The marriage fund is no longer 100000"); break; } } } } } package com.myvolatile2; public class MyThread2 extends Thread { @Override public void run() { synchronized (Money.lock) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } Money.money = 90000; } } }
5.4 atomicity
Atomicity means that in one operation or multiple operations, either all operations are executed and will not be interrupted by any factor, or all operations are not executed. Multiple operations are an inseparable whole.
public class AtomDemo { public static void main(String[] args) { MyAtomThread atom = new MyAtomThread(); for (int i = 0; i < 100; i++) { new Thread(atom).start(); } } } class MyAtomThread implements Runnable { private volatile int count = 0; //Quantity of ice cream delivered @Override public void run() { for (int i = 0; i < 100; i++) { //1. Read data from shared data to this thread stack //2. Modify the value of the variable copy in the thread stack //3. The value of the variable copy in the thread stack will be assigned to the shared data count++; System.out.println("Already" + count + "Ice cream"); } } }
count + + is not an atomic operation. It may be interrupted by other threads during execution
5.5volatile cannot guarantee atomicity
We can add locks to the count + + operation, so the count + + operation is the code in the critical area. The code in the critical area can only be executed by one thread at a time, so count + + becomes an atomic operation.
public class AtomDemo { public static void main(String[] args) { MyAtomThread atom = new MyAtomThread(); for (int i = 0; i < 100; i++) { new Thread(atom).start(); } } } class MyAtomThread implements Runnable { private volatile int count = 0; //Quantity of ice cream delivered private Object lock = new Object(); @Override public void run() { for (int i = 0; i < 100; i++) { //1. Read data from shared data to this thread stack //2. Modify the value of the variable copy in the thread stack //3. The value of the variable copy in the thread stack will be assigned to the shared data synchronized (lock) { count++; System.out.println("Already" + count + "Ice cream"); } } } }
5.6 atomic AtomicInteger
There are many types of quantities, so 13 classes are provided in the Atomic package, belonging to four types of Atomic update methods, namely, Atomic update basic type, Atomic update array, Atomic update reference and Atomic update attribute (field). This time, we will only explain how to update basic types using atoms. The Atomic package provides the following three classes:
AtomicBoolean: atomic update boolean type
AtomicInteger: atomic update integer
AtomicLong: atomic update long
The above 3 classes as like as two peas are provided, so this section is only explained by AtomicInteger. The common methods of AtomicInteger are as follows:
public AtomicInteger(): Initializes an atomic type with a default value of 0 Integer public AtomicInteger(int initialValue): Initializes an atomic type of the specified value Integer int get(): Get value int getAndIncrement(): Add 1 to the current value atomically. Note that the value before self increment is returned here. int incrementAndGet(): Add 1 to the current value atomically. Note that the value returned here is the value after self increment. int addAndGet(int data): Atomically compares the entered value with the value in the instance( AtomicInteger Inside value)Add and return the result. int getAndSet(int value): Set atomically to newValue And returns the old value.
6. Concurrent tools
6.1Hashtable
Reasons for the emergence of Hashtable: in collection classes, HashMap is a common collection object, but HashMap is thread unsafe (there may be problems in multi-threaded environment). In order to ensure the security of data, we can use Hashtable, but Hashtable is inefficient.
package com.mymap; import java.util.HashMap; import java.util.Hashtable; public class MyHashtableDemo { public static void main(String[] args) throws InterruptedException { Hashtable<String, String> hm = new Hashtable<>(); Thread t1 = new Thread(() -> { for (int i = 0; i < 25; i++) { hm.put(i + "", i + ""); } }); Thread t2 = new Thread(() -> { for (int i = 25; i < 51; i++) { hm.put(i + "", i + ""); } }); t1.start(); t2.start(); System.out.println("----------------------------"); //In order to add all the data t1 and t2 Thread.sleep(1000); //0-0 1-1 ..... 50- 50 for (int i = 0; i < 51; i++) { System.out.println(hm.get(i + "")); }//0 1 2 3 .... 50 } }
6.2ConcurrentHashMap
Reasons for the occurrence of ConcurrentHashMap: in collection classes, HashMap is a common collection object, but HashMap is thread unsafe (there may be problems in multi-threaded environment). In order to ensure the security of data, we can use Hashtable, but Hashtable is inefficient.
1. HashMap is thread unsafe. There will be data security problems in multithreaded environment
2. Hashtable is thread safe, but it will lock the whole table, which is inefficient
3. Concurrent HashMap is also thread safe and efficient. In JDK7 and JDK8, the underlying principle is different.
package com.mymap; import java.util.Hashtable; import java.util.concurrent.ConcurrentHashMap; public class MyConcurrentHashMapDemo { public static void main(String[] args) throws InterruptedException { ConcurrentHashMap<String, String> hm = new ConcurrentHashMap<>(100); Thread t1 = new Thread(() -> { for (int i = 0; i < 25; i++) { hm.put(i + "", i + ""); } }); Thread t2 = new Thread(() -> { for (int i = 25; i < 51; i++) { hm.put(i + "", i + ""); } }); t1.start(); t2.start(); System.out.println("----------------------------"); //In order to add all the data t1 and t2 Thread.sleep(1000); //0-0 1-1 ..... 50- 50 for (int i = 0; i < 51; i++) { System.out.println(hm.get(i + "")); }//0 1 2 3 .... 50 } }
6.3CountDownLatch
CountDownLatch class:
method | explain |
---|---|
public CountDownLatch(int count) | Parameter number of passing threads, indicating the number of waiting threads |
public void await() | Let the thread wait |
public void countDown() | The current thread has finished executing |
Let a thread wait for other threads to execute before executing
public class ChileThread1 extends Thread { private CountDownLatch countDownLatch; public ChileThread1(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { //1. Eat dumplings for (int i = 1; i <= 10; i++) { System.out.println(getName() + "Eating the first" + i + "A dumpling"); } //2. Say it after eating //Every time the countDown method is, the counter is set to - 1 countDownLatch.countDown(); } } public class ChileThread2 extends Thread { private CountDownLatch countDownLatch; public ChileThread2(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { //1. Eat dumplings for (int i = 1; i <= 15; i++) { System.out.println(getName() + "Eating the first" + i + "A dumpling"); } //2. Say it after eating //Every time the countDown method is, the counter is set to - 1 countDownLatch.countDown(); } } public class ChileThread3 extends Thread { private CountDownLatch countDownLatch; public ChileThread3(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { //1. Eat dumplings for (int i = 1; i <= 20; i++) { System.out.println(getName() + "Eating the first" + i + "A dumpling"); } //2. Say it after eating //Every time the countDown method is, the counter is set to - 1 countDownLatch.countDown(); } } public class MotherThread extends Thread { private CountDownLatch countDownLatch; public MotherThread(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { //1. Wait try { //When the counter becomes 0, it will automatically wake up the waiting thread here. countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } //2. Clean up the dishes and chopsticks System.out.println("Mother is cleaning up the dishes"); } } public class MyCountDownLatchDemo { public static void main(String[] args) { //1. To create an object of CountDownLatch, you need to pass it to four threads. //A counter is defined at the bottom, and the value of the counter is 3 CountDownLatch countDownLatch = new CountDownLatch(3); //2. Create four thread objects and open them. MotherThread motherThread = new MotherThread(countDownLatch); motherThread.start(); ChileThread1 t1 = new ChileThread1(countDownLatch); t1.setName("Xiao Ming"); ChileThread2 t2 = new ChileThread2(countDownLatch); t2.setName("Xiao Hong"); ChileThread3 t3 = new ChileThread3(countDownLatch); t3.setName("Xiao Gang"); t1.start(); t2.start(); t3.start(); } }
1. CountDownLatch(int count): parameter the number of write wait threads. A counter is defined.
2. await(): let the thread wait. When the counter is 0, the waiting thread will wake up
3. countDown(): called when the thread finishes executing, the counter will be - 1.
6.4Semaphore
You can control the number of threads accessing a particular resource.
1. Someone needs to manage this channel
2. When a car comes in, issue a pass
3. When the car goes out, withdraw the pass
4. If the permit is issued, other vehicles can only wait
public class MyRunnable implements Runnable { //1. Obtain the administrator object, private Semaphore semaphore = new Semaphore(2); @Override public void run() { //2. Obtain a pass try { semaphore.acquire(); //3. Start driving System.out.println("Get a pass and start driving"); Thread.sleep(2000); System.out.println("Return the pass"); //4. Return of pass semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } public class MySemaphoreDemo { public static void main(String[] args) { MyRunnable mr = new MyRunnable(); for (int i = 0; i < 100; i++) { new Thread(mr).start(); } } }
---------------------------------------------------------------------------------------------------------------------------------
Some of the contents are recorded in books, classes and networks. If they are the same, it is a coincidence