“As the saying goes, he who knows multithreading wins.”
If you understand multithreading thoroughly, getting a raise is not a problem at all
Implementing multithreading
Learn about multithreading
It refers to the technology that implements concurrent execution of multiple threads from software or hardware. Multithreaded computers have hardware support that allows them to execute multiple threads at the same time, improving performance.
Concurrency and parallelism
- Parallel: Multiple instructions are executed simultaneously on multiple cpus at the same time.
- Concurrency: Multiple instructions are executed alternately on a single CPU at the same time.
Processes and threads
-
Process: is a running program
Independence: process is a basic unit that can run independently, and it is also an independent unit for the system to allocate resources and schedule. Dynamic: The essence of a process is the execution process of a program. Process is dynamically generated and dynamically eliminated concurrency: any process can be executed concurrently with other processes
-
Thread: a single sequence control flow in a process, an execution path, and an execution scenario/execution unit in a process.
Single-threaded: A process that has only one path of execution is called a single-threaded program
Multithreading: IF a process has more than one execution path, it is called multithreaded program
Implementation of multithreading way 1: inherit Thread class 【 application 】
-
Methods to introduce
The method name instructions void run() This method is called to execute after the thread is started void start() To start the thread executing, the Java virtual machine calls the run method () -
Implementation steps
- Define a class MyThread that inherits Thread
- Override the run() method in MyThread
- Create an object of the MyThread class
- Starting a thread
-
Code demo
public class MyThread extends Thread {
@Override
public void run(a) {
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 the thread to start executing; The Java virtual machine calls the run method of this threadmy1.start(); my2.start(); }}Copy the code
-
Two little questions
-
Why override the run() method?
Because run() is used to encapsulate code that is being executed by threads
-
What is the difference between the run() and start() methods?
Run () : encapsulates code executed by a thread and calls it directly, equivalent to a normal method call, without starting the thread
Start () : starts the thread; The JVM then calls the thread’s run() method
-
Implementation of multithreading way two: implementation Runnable interface [application]
-
Thread constructor
The method name instructions Thread(Runnable target) Allocate a new Thread object Thread(Runnable target, String name) Allocate a new Thread object -
Implementation steps
- Define a class MyRunnable that implements the Runnable interface
- Override the run() method in the MyRunnable class
- Create an object of the MyRunnable class
- Create an object of the Thread class, taking the MyRunnable object as a constructor parameter
- Starting a thread
-
Code demo
public class MyRunnable implements Runnable {
@Override
public void run(a) {
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 class MyRunnable
MyRunnable my = new MyRunnable();
// Create an object of the Thread class with MyRunnable as the constructor argument
//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,"Plane");
// Start the threadt1.start(); t2.start(); }}Copy the code
Implementation of multithreading way three: implementation of Callable interface
-
Methods to introduce
The method name instructions V call() Evaluates the result, and if it cannot, throws an exception FutureTask(Callable callable) Create a FutureTask that executes the given Callable once it runs V get() If necessary, wait for the calculation to complete and then retrieve its results -
FutureTask is like an intermediate class that implements interfaces, creates Thread threads, and retrieves Thread return values
-
The advantage of implementing the Callable interface is that the thread execution results can be obtained. Disadvantages of this method: It is inefficient to obtain the results of t thread execution, the current thread is blocked, low efficiency. java.util.concurrent.FutureTask; JUC package, belongs to the Java package, the old JDK does not have this package. New features.
-
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 FutureTask, the implementation class for the Future, with MyCallable as a constructor parameter
- Create an object of the Thread class, taking the FutureTask object as a constructor parameter
- Starting a thread
- Call the get method to get the result after the thread ends.
-
Code demo
public class MyCallable implements Callable<String> {
@Override
public String call(a) throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("Tell a girl you love her." + i);
}
// The return value represents the result after the thread has finished running
return "Promise"; }}public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// After the thread is started, it needs to execute the call method inside
MyCallable mc = new MyCallable();
//Thread t1 = new Thread(mc);
// We can get the result after the thread has finished executing. It can also be passed to Thread objects as arguments
FutureTask<String> ft = new FutureTask<>(mc);
// Create a thread object
Thread t1 = new Thread(ft);
String s = ft.get();
// Start the thread
t1.start();
//String s = ft.get();System.out.println(s); }}Copy the code
-
Comparison of three implementations
- Implement Runnable and Callable interfaces
- Benefits: Strong extensibility, while implementing this interface can also inherit other classes
- Disadvantages: Programming is relatively complex and you can’t use methods in Thread directly
- Thread class inheritance
- Benefits: Programming is simple and you can use the methods in the Thread class directly
- Disadvantages: Poor extensibility, cannot inherit from other classes
- Implement Runnable and Callable interfaces
Set and get the thread name
-
Methods to introduce
The method name instructions 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 -
Code demo
public class MyThread extends Thread {
public MyThread(a) {}
public MyThread(String name) {
super(name);
}
@Override
public void run(a) {
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();
// Threads have default names in the format of thread-number
//void setName(String name) : Changes the name of this thread to equal the parameter name
my1.setName("High-speed rail");
my2.setName("Plane");
//Thread(String name)
MyThread my1 = new MyThread("High-speed rail");
MyThread my2 = new MyThread("Plane");
The constructor can also give the thread a name, but it writes out the thread's no-argument and parameterized constructors
/*class MyThread extends Thread{ public MyThread(String name) { super(name); } public MyThread() {@override public void run(){// execute the code}} */
my1.start();
my2.start();
Static Thread currentThread() returns a reference to the Thread object currently executingSystem.out.println(Thread.currentThread().getName()); }}Copy the code
Thread sleep [application]
-
Relevant methods
The method name instructions static void sleep(long millis) Causes the currently executing thread to pause (pause execution) for the specified number of milliseconds -
Code demo
public class MyRunnable implements Runnable {
@Override
public void run(a) {
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 (); /* system.out.println (); Thread.sleep(3000); System.out.println(" wake up "); * /
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = newThread(mr); t1.start(); t2.start(); }}Copy the code
Thread priority [application]
-
Thread scheduling
-
Two scheduling methods
- Time-sharing scheduling model: All threads take turns to use the CPU and allocate the CPU time slice equally for each thread
- Preemptive scheduling model: priority is given to threads with higher priority to use CPU. If threads have the same priority, one thread will be randomly selected, and the thread with higher priority will get more CPU time slices
-
Java uses a preemptive scheduling model
-
randomness
If the computer has only one CPU, the CPU can execute only one instruction at a time, and threads can execute instructions only if they are given a slice of CPU time, which is the right to use it. So the execution of multithreaded programs is random, because who grabs the CPU is not necessarily right
-
-
Priority dependent methods
The method name instructions final int getPriority() Returns the priority of this thread final void setPriority(int newPriority) Change the priority of this thread Thread default priority is 5; Thread priorities range from 1 to 10 -
Note that thread priorities range from 1 to 10. The default priority for threads is 5. This only increases the likelihood that threads will preempt CPU execution, but does not necessarily increase the actual preemption rate
-
Code demo
public class MyCallable implements Callable<String> {
@Override
public String call(a) throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
}
return "Thread completes execution."; }}public class Demo {
public static void main(String[] args) {
// Priority: 1-10 Default value :5
MyCallable mc = new MyCallable();
FutureTask<String> ft = new FutureTask<>(mc);
Thread t1 = new Thread(ft);
t1.setName("Plane");
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()); / / 5t2.start(); }}Copy the code
Daemon thread [application]
-
Relevant methods
The method name instructions void setDaemon(boolean on) Mark this thread as a daemon thread, and when all running threads are daemons, the Java virtual machine exits -
Code demo
public class MyThread1 extends Thread {
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "-"+ i); }}}public class MyThread2 extends Thread {
@Override
public void run(a) {
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
// There is no need for the daemon thread to continue running after the normal thread has finished executing.
t2.setDaemon(true); t1.start(); t2.start(); }}Copy the code
Thread merging [application]
The method name | instructions |
---|---|
public final void join() | To merge a thread into a current thread, block the current thread, and execute until it finishes. |
public static void main(String[] args) {
System.out.println("main begin");
Thread t = new Thread(new MyRunnable7());
t.setName("t");
t.start();
// Merge threads
try {
t.join(); // t is merged into the current thread, the current thread is blocked, and the t thread executes until it finishes.
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main over");
}
Copy the code
Give way to threads [application]
The method name | instructions |
---|---|
static void yield() | Suspends the currently executing thread object and executes the other threads |
- The yield() method is not a blocking method. Makes the current thread yield to another thread.
- The execution of the yield() method causes the current thread to return from the “running” state to the “ready state.”
- Note: it is possible to grab it again after getting ready.
/* Make way for the current thread to pause and return to the ready state to give way to another thread. Static methods: thread.yield (); * /
public class ThreadTest12 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable6());
t.setName("t");
t.start();
for(int i = 1; i <= 10000; i++) {
System.out.println(Thread.currentThread().getName() + "- >"+ i); }}}class MyRunnable6 implements Runnable {
@Override
public void run(a) {
for(int i = 1; i <= 10000; i++) {
// Every 100 seats.
if(i % 100= =0){
Thread.yield(); // The current thread pauses to give way to the main thread.
}
System.out.println(Thread.currentThread().getName() + "- >"+ i); }}}Copy the code
The Timer Timer
The method name | instructions |
---|---|
void schedule(TimerTask task, Date time) | Schedule the execution of the specified task at the specified time. |
void schedule(TimerTask task, Date firstTime, long period) | Repeated fixed-delay execution for the specified task, starting at the specified time |
/* Use a timer to specify a scheduled task. * /
public class TimerTest {
public static void main(String[] args) throws Exception {
// Create a timer object
Timer timer = new Timer();
//Timer timer = new Timer(true); // how to daemon a thread
// Specify a scheduled task
//timer.schedule(scheduled task, first execution time, how often);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date firstTime = sdf.parse("The 2020-03-14 09:34:30");
//timer.schedule(new LogTimerTask() , firstTime, 1000 * 10);
// Once a year.
//timer.schedule(new LogTimerTask() , firstTime, 1000 * 60 * 60 * 24 * 365);
// Anonymous inner class
timer.schedule(new TimerTask(){
@Override
public void run() {
// code....
}
} , firstTime, 1000 * 10); }}Write a scheduled task class
// Suppose this is a timed task that logs
class LogTimerTask extends TimerTask {
@Override
public void run(a) {
// Just write the tasks you need to perform.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String strTime = sdf.format(new Date());
System.out.println(strTime + ": Successfully completed a data backup!"); }}Copy the code
Thread synchronization
Sell tickets
-
Case needs
A cinema is currently showing domestic blockbusters, a total of 100 tickets, and it has 3 Windows to sell tickets, please design a program to simulate the cinema to sell tickets
-
Implementation steps
-
Define a class SellTicket to implement the Runnable interface, which defines a member variable: private int tickets = 100;
-
Rewrite the run() method to sell tickets in the SellTicket class as follows
-
If the number of votes is greater than zero, sell the tickets and tell them which window they are from
-
After the tickets are sold, the total number of votes is reduced by one
-
Tickets sold out, threads stopped
-
Define a test class, SellTicketDemo, with the main method as follows
-
Create an object of the SellTicket class
-
Create objects of the three Thread classes, take the SellTicket object as an argument to the constructor, and give the corresponding window name
-
Starting a thread
-
-
Code implementation
public class SellTicket implements Runnable {
private int tickets = 100;
// Override the run() method in the SellTicket class to sell tickets
@Override
public void run(a) {
while (true) {
if(ticket <= 0) {/ / sold out
break;
}else{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "They're selling tickets. There's more." + ticket + "Ticket"); }}}}public class SellTicketDemo {
public static void main(String[] args) {
// Create an object of class SellTicket
SellTicket st = new SellTicket();
// Create three objects of the Thread class, taking the SellTicket object as an argument to the constructor, and giving 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 the threadt1.start(); t2.start(); t3.start(); }}Copy the code
The problem of ticket selling cases
-
There was a problem selling tickets
-
The same ticket shows up multiple times
-
There are negative tickets
-
-
Cause of the problem
The randomness of thread execution can result in a loss of CPU execution during ticket sales, causing problems
Synchronizing code Blocks to solve data security Problems
-
Conditions under which security problems arise
-
It’s a multi-threaded environment
-
There is shared data
-
Multiple statements operate on shared data
-
-
How to solve the multithreaded security problem?
- Basic idea: let the program have no security problem environment
-
How do you do that?
-
Lock up code that operates on shared data so that only one thread can execute it at any time
-
Java provides a way to solve this problem by synchronizing blocks of code
-
-
Synchronous code block format:
Synchronized (arbitrary object) {code in which multiple statements operate on shared data}Copy the code
- Note that the synchronized code block must lock objects shared by multiple threads, otherwise it will not work. Synchronized: You lock your code, and any object can be considered a lock
public class test33 {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
MyThread thread3 = new MyThread();
thread1.setName(Thread 1 "");
thread2.setName(Thread 2 "");
thread3.setName("Thread 3"); thread1.start(); thread2.start(); thread3.start(); }}class MyThread extends Thread {
private static int ticket = 100;
public Object obj = new Object();
@Override
public void run(a) {
while (true) {
synchronized (obj) {
if (ticket <= 0) break;
else {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "Executed, and the rest." + ticket + "Ticket");
}
}
}
}
}
Copy the code
In this case, the three threads created have three different Object member objects, which are not shared, equivalent to not locked, invalid. Public static Object obj = new Object(); 'Make obj a shared member object. In this case, this and 'synchronized(this)' locks are still invalid because the object referred to by this is not shared.Copy the code
-
The benefits and drawbacks of synchronization
-
Benefits: Solve multi-threaded data security problems
-
Disadvantages: When there are many threads, because each thread has to determine the lock on the synchronization, this is very expensive, virtually reduces the efficiency of the application
-
-
Code demo
public class SellTicket implements Runnable {
private int tickets = 100;
private Object obj = new Object();
@Override
public void run(a) {
while (true) {
synchronized (obj) { // To lock potentially unsafe code, multiple threads must use the same lock
// when t1 comes in, it locks the code
if (tickets > 0) {
try {
Thread.sleep(100);
//t1 rest for 100 ms
} catch (InterruptedException e) {
e.printStackTrace();
}
// Window 1 is selling the 100th ticket
System.out.println(Thread.currentThread().getName() + "For sale" + 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(); }}Copy the code
Synchronization method to Solve data Security Problems
-
The format of the synchronization method
Synchronized methods: add the synchronized keyword to a method
Synchronized return value type method name (method parameter) {method body; }Copy the code
What is the lock object of the synchronization method? thisCopy the code
-
Statically synchronized method
Static methods: Add the synchronized keyword to static methods
The modifierstaticSynchronized return value type method name (method parameter) {method body; }Copy the code
What is the lock object for a synchronous static method? The name of the class. The classCopy the code
- Code demo
public class MyRunnable implements Runnable {
private static int ticketCount = 100;
@Override
public void run(a) {
while(true) {if("Window one".equals(Thread.currentThread().getName())){
// Synchronize methods
boolean result = synchronizedMthod();
if(result){
break; }}if("Window two".equals(Thread.currentThread().getName())){
// Synchronize code blocks
synchronized (MyRunnable.class){
if(ticketCount == 0) {break;
}else{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "They're selling tickets. There's more." + ticketCount + "Ticket");
}
}
}
}
}
private static synchronized boolean synchronizedMthod(a) {
if(ticketCount == 0) {return true;
}else{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "They're selling tickets. There's more." + ticketCount + "Ticket");
return false; }}}Copy the code
The test classCopy the code
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 two"); t1.start(); t2.start(); }}Copy the code
Lock 【 application 】
While we can understand the Lock object problem with synchronized code blocks and methods, we do not directly see where the Lock is placed and where the Lock is released. In order to make it clearer, 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. The current object is locked by default
-
ReentrantLock constructor
The method name instructions ReentrantLock() Create an instance of ReentrantLock -
Lock unlocking method
The method name instructions void lock() Gets the lock void unlock() Release the lock -
Code demo
public class Ticket implements Runnable {
// The number of tickets
private int ticket = 100;
private Object obj = new Object();
private ReentrantLock lock = new ReentrantLock();
@Override
public void run(a) {
while (true) {
Synchronized (obj){// Multiple threads must use the same lock.
try {
lock.lock();
if (ticket <= 0) {
/ / sold out
break;
} else {
Thread.sleep(100);
ticket--;
System.out.println(Thread.currentThread().getName() + "They're selling tickets. There's more." + 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 two");
t3.setName("Window three"); t1.start(); t2.start(); t3.start(); }}Copy the code
Deadlocks
-
An overview of the
Thread deadlocks occur when two or more threads hold resources needed by each other, so that the threads are in a waiting state and cannot execute
-
When can a deadlock occur
- Resources co., LTD.
- Synchronous nesting (nesting of locks)
-
Code demo
public class Demo {
public static void main(String[] args) {
Object objA = new Object();
Object objB = new Object();
new Thread(()->{
while(true){
synchronized (objA){
/ / thread
try {
Thread.sleep(100); // Hibernate for thread 2 to acquire the lock objB
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objB){
System.out.println("Well-off students are walking.");
}
}
}
}).start();
new Thread(()->{
while(true){
synchronized (objB){
/ / thread 2
try {
Thread.sleep(100); // Hibernate for thread 1 to acquire the lock objA
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objA){
System.out.println("Wei is walking."); } } } }).start(); }}Copy the code
Producer consumer
Overview of producer and Consumer Models
-
An overview of the
The producer-consumer model is a very classic multi-thread collaboration model. Understanding the producer-consumer problem can make us understand the multi-thread programming more deeply.
The so-called producer-consumer problem actually consists of two main types of threads:
One is the producer thread used to produce data
One is the consumer thread used to consume data
To decouple producers and consumers, a shared data area is often used, like a warehouse
Producers produce the data and place it directly in the shared data area without concern for consumer behavior
Consumers only need to obtain data from the shared data area, and do not need to care about the behavior of the producer
-
The wait and wake methods of the Object class
The method name instructions void wait() Causes the current thread to wait until another thread calls notify() or notifyAll() on the object void notify() Wakes up a single thread that is waiting for the object monitor void notifyAll() Wakes up all threads that are waiting on the object monitor -
Wait () causes t to wait on o and releases the lock on o previously held by t.
-
Notify. Tify () awakens a thread that is waiting on an O object and does not release a previously held lock on the o object.
Producer and Consumer Cases
-
Case needs
-
Desk class: define the variable that represents the number of buns, define the lock object variable, define the variable that marks the presence of buns on the table
-
The producer class Cooker: Implements the Runnable interface, overrides the run() method, and sets up thread tasks
1. Determine if there is a packet and determine whether the current thread is running
2. If there are steamed stuffed bun, enter the waiting state, if there is no steamed stuffed bun, continue to execute, produce steamed stuffed bun
3. After the production of steamed stuffed bun, update the state of steamed stuffed bun on the table to awaken consumers to consume steamed stuffed bun
-
Consumer class (Foodie) : Implements the Runnable interface, overrides the run() method, and sets up thread tasks
1. Determine if there is a packet and determine whether the current thread is running
2. If there is no baozi, enter the waiting state, if there is baozi, consume baozi
3. After consuming steamed stuffed bun, update the status of steamed stuffed bun on the table and wake up the producer to produce steamed stuffed bun
-
Test class (Demo) : there is a main method, the main method in the code steps as follows
Create producer thread and consumer thread objects
Start the two threads separately
-
-
Code implementation
public class Desk {
// Define a tag
//true: if there are hamburgers on the table, foodies are allowed
//false indicates that there is no hamburger on the table, allowing the chef 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 step:
// 1, check if there are hamburgers on the table
// If there is, wait; if not, produce.
// 2, put the hamburger on the table.
// 3, wake up the waiting consumers and start eating.
@Override
public void run(a) {
while(true){
synchronized (Desk.lock){
if(Desk.count == 0) {break;
}else{
if(! Desk.flag){/ / production
System.out.println("The chef 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(a) {
// 1, check if there are hamburgers on the table.
// 2, if not, wait.
// 3, if you have, start eating
// 4 after eating, there are no hamburgers on the table
// Wake up the waiting producer to resume production
// Subtract one from the total number of hamburgers
/ / format:
//1. While (true) an infinite loop
//2. Synchronized lock
//3. Check whether data sharing is complete. The end of the
//4. Check whether data sharing is complete. Not the end
while(true){
synchronized (Desk.lock){
if(Desk.count == 0) {break;
}else{
if(Desk.flag){
/ / a
System.out.println("Foodies are eating hamburgers.");
Desk.flag = false;
Desk.lock.notifyAll();
Desk.count--;
}else{
// If not, wait
// The wait and wake methods must be called using the lock object.
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
/* Consumer step: 1, determine if there is a hamburger on the table. 2. If not, wait. 3, if there is, open to eat 4, after eating, there are no hamburgers on the table wake up the producer to continue to produce the total number of hamburgers minus 1 */
/* Producer steps: 1, determine if there is a hamburger on the table, if there is, wait, if not, produce. 2. Put the hamburgers on the table. 3. Wake up waiting consumers and start eating. * /
Foodie f = new Foodie();
Cooker c = newCooker(); f.start(); c.start(); }}Copy the code
Optimization of Producer and Consumer Cases
-
demand
- Encapsulate the variables in the Desk class in an object-oriented way
- 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, passing in the Desk class object in the constructor
- Start two threads
-
Code implementation
public class Desk {
// Define a tag
//true: if there are hamburgers on the table, foodies are allowed
//false indicates that there is no hamburger on the table, allowing the chef to execute
//public static boolean flag = false;
private boolean flag;
// Total number of hamburgers
//public static int count = 10;
// In the future we are using the 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(a) {
this(false.10); // Call the parameter inside the empty parameter, assign the value to the member variable, and then use the member variable directly
}
public Desk(boolean flag, int count) {
this.flag = flag;
this.count = count;
}
public boolean isFlag(a) {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getCount(a) {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Object getLock(a) {
return lock;
}
@Override
public String toString(a) {
return "Desk{" +
"flag=" + flag +
", count=" + count +
", lock=" + lock +
'} '; }}public class Cooker extends Thread {
private Desk desk;
public Cooker(Desk desk) {
this.desk = desk;
}
// Producer step:
// 1, check if there are hamburgers on the table
// If there is, wait; if not, produce.
// 2, put the hamburger on the table.
// 3, wake up the waiting consumers and start eating.
@Override
public void run(a) {
while(true){
synchronized (desk.getLock()){
if(desk.getCount() == 0) {break;
}else{
// system.out.println (" verify "); // system.out.println (" verify ");
if(! desk.isFlag()){/ / production
System.out.println("The chef 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(a) {
// 1, check if there are hamburgers on the table.
// 2, if not, wait.
// 3, if you have, start eating
// 4 after eating, there are no hamburgers on the table
// Wake up the waiting producer to resume production
// Subtract one from the total number of hamburgers
/ / format:
//1. While (true) an infinite loop
//2. Synchronized lock
//3. Check whether data sharing is complete. The end of the
//4. Check whether data sharing is complete. Not the end
while(true){
synchronized (desk.getLock()){
if(desk.getCount() == 0) {break;
}else{
// system.out.println (" verify "); // system.out.println (" verify ");
if(desk.isFlag()){
/ / a
System.out.println("Foodies are eating hamburgers.");
desk.setFlag(false);
desk.getLock().notifyAll();
desk.setCount(desk.getCount() - 1);
}else{
// If not, wait
// The wait and wake methods must be called using the lock object.
try {
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
/* Consumer step: 1, determine if there is a hamburger on the table. 2. If not, wait. 3, if there is, open to eat 4, after eating, there are no hamburgers on the table wake up the producer to continue to produce the total number of hamburgers minus 1 */
/* Producer steps: 1, determine if there is a hamburger on the table, if there is, wait, if not, produce. 2. Put the hamburgers on the table. 3. Wake up waiting consumers and start eating. * /
Desk desk = new Desk();
Foodie f = new Foodie(desk);
Cooker c = newCooker(desk); f.start(); c.start(); }}Copy the code
Blocking queue basic use
- Blocking queue inheritance structure
-
Common BlockingQueue:
ArrayBlockingQueue: The bottom layer is an array, bounded
LinkedBlockingQueue: The underlying list is unbounded. But it’s not really unbounded, it’s the maximum value of int
-
The core method of BlockingQueue:
Put (anObject): Puts a parameter into a queue. If it is not put into a queue, it will block
Take (): Fetches the first data, failing which blocks
-
Code sample
public class Demo02 {
public static void main(String[] args) throws Exception {
// Create a blocking queue object of size 1
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);
// Store elements
arrayBlockingQueue.put("Hamburger");
/ / element
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take()); // It will be blocked
System.out.println("The procedure is over."); }}Copy the code
Blocking queue implementation wake-up mechanism
-
Case needs
-
The producer class Cooker: Implements the Runnable interface, overrides the run() method, and sets up thread tasks
1. Receive a blocking queue object in the constructor
2. Loop in the run method to add the bun to the blocking queue
3. Print the result
-
Consumer class (Foodie) : Implements the Runnable interface, overrides the run() method, and sets up thread tasks
1. Receive a blocking queue object in the constructor
2. Loop in the run method to get the bun in the blocking queue
3. Print the result
-
Test class (Demo) : there is a main method, the main method in the code steps as follows
Create a blocking queue object
Create producer thread and consumer thread objects, passing in blocking queue objects in the constructor
Start the two threads separately
-
-
Code implementation
public class Cooker extends Thread {
private ArrayBlockingQueue<String> bd;
public Cooker(ArrayBlockingQueue<String> bd) {
this.bd = bd;
}
// Producer step:
// 1, check if there are hamburgers on the table
// If there is, wait; if not, produce.
// 2, put the hamburger on the table.
// 3, wake up the waiting consumers and start eating.
@Override
public void run(a) {
while (true) {
try {
bd.put("Hamburger");
System.out.println("The chef 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(a) {
// 1, check if there are hamburgers on the table.
// 2, if not, wait.
// 3, if you have, start eating
// 4 after eating, there are no hamburgers on the table
// Wake up the waiting producer to resume production
// Subtract one from the total number of hamburgers
/ / format:
//1. While (true) an infinite loop
//2. Synchronized lock
//3. Check whether data sharing is complete. The end of the
//4. Check whether data sharing is complete. Not the end
while (true) {
try {
String take = bd.take();
System.out.println("Foodie 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 = newCooker(bd); f.start(); c.start(); }}Copy the code
Note: The final print may repeat the same statement, which is not expected. This is because the underlying source code for ArrayBlockingQueue is locked with a ReentrantLock to implement thread synchronization, but the System.out statement is our own, and the print statement does not implement thread synchronization, so there may be a problem with printing. This is a minor problem.