Phase 5 Multithreading
Preface:
A scene: Weekend, with no female tickets to go to the movies, whether it is live tickets or mobile phone tickets, there is still a seat in the last second. After a moment of delay, it shows that the seat has been unable to be selected. If you don't pay attention to it, there will be no seat. The tickets of the cinema are certain, but exactly how to do it. To, multiple windows or users at the same time without repeating the ticket? That's the multithreading problem we're going to talk about today.
(1) An overview of threads and processes
(1) Process
- Process: A process is an independent unit of the system for resource allocation and invocation. Each process has its own memory space and system resources
- Multithreading: Multiple tasks can be performed in the same time period, which improves CPU utilization.
(2) Threads
-
Thread: Execution unit of process, execution path
-
Single thread: An application has only one execution path
-
Multithreading: An application has multiple execution paths
-
What is the significance of multi-process? —— Increasing CPU usage
-
What is the meaning of multithreading? —— Improving application usage
(3) Supplement
Parallel and concurrent
- Parallelism occurs logically at the same time, referring to running multiple programs at the same time in a certain period of time.
- Concurrency is physical concurrency, which means running multiple programs at the same time at a certain point in time.
Are the principles of Java programming and JVM startup multithreaded?
-
Running Principles of Java Programs:
- Starting a JVM from a java command is equivalent to starting a process
- Then the process creates a main thread to call the main method.
-
Is the startup of the JVM virtual machine single-threaded or multi-threaded?
- Garbage collection threads should also be started first, otherwise memory overflow will easily occur.
- Now the garbage collection threads plus the previous main threads start at least two threads, so the start of jvm is actually multi-threaded.
- The JVM startup starts at least the garbage collection thread and the main thread, so it is multithreaded.
(2) Implementation of multithreaded code
Requirement: We need to implement multithreaded programs.
How to achieve it?
Since threads are process-dependent, we should create a process first.
The process is created by the system, so we should call the system function to create a process.
Java can't call system functions directly, so we can't implement multithreaded programs directly.
But what? Java can call programs written in C/C++ to implement multithreaded programs.
C/C++ calls system functions to create processes, and Java calls such things.
Then we provide some classes for our use. We can implement multithreaded programs.
By looking at the API, we know that there are two ways to implement multithreaded programs.
Mode 1: Inherit the Thread class
Steps:
-
Custom MyThread (custom class name) inherits the Thread class
-
Override run() in the MyThread class
-
create object
-
Start threads
public class MyThread extends Thread{ public MyThread() { } @Override public void run() { for (int i = 0; i < 100; i++){ System.out.println(getName() + ": " + i); } } }
public class MyThreadTest { public static void main(String[] args) { //Creating Thread Objects MyThread my = new MyThread(); //Start threads, run() is equivalent to the call of ordinary methods, single-threaded effect //my.run(); //First, the thread is started, and then the run() method of the thread is called by jvm. Multithread effect my.start(); //Two threads demonstrate that the multithreading effect requires creating multiple objects rather than calling the start() method multiple times by one object MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); my1.start(); my2.start(); } } //Operation results Thread-1: 0 Thread-1: 1 Thread-1: 2 Thread-0: 0 Thread-1: 3 Thread-0: 1 Thread-0: 2 ...... Thread-0: 95 Thread-0: 96 Thread-0: 97 Thread-0: 98 Thread-0: 99
Mode 2: Implementing Runnable Interface (Recommendation)
Steps:
- Implementation of Runnable Interface with Custom Class MyuRunnable
- Override run() method
- Creating objects of MyRunable class
- Create an object of the Thread class and pass the object of step C as a construction parameter
public class MyRunnable implements Runnable { public MyRunnable() { } @Override public void run() { for (int i = 0; i < 100; i++){ //Because the way to implement the interface can't use the method of Thread class directly, but it can be used indirectly. System.out.println(Thread.currentThread().getName() + ": " + i); } } }
public class MyRunnableTest { public static void main(String[] args) { //Create objects of MyRunnable class MyRunnable my = new MyRunnable(); //Create an object of the Thread class and pass the object of step C as a construction parameter // Thread t1 = new Thread(my); // Thread t2 = new Thread(my); //Here's how to set the thread object name // t1.setName("User1"); // t1.setName("User2"); Thread t1 = new Thread(my,"User1"); Thread t2 = new Thread(my,"User2"); t1.start() t2.start(); } }
The Benefits of Implementing Interface Approaches
Avoid limitations due to Java's single inheritance
Suitable for multiple codes of the same program to deal with the same resource, effectively separating threads from program codes and data, better reflects the object-oriented design idea.
How to Understand - --- To Avoid the Limitations Caused by Java Single Inheritance
For example, a class already has a parent class, and this class wants to implement multithreading, but at this time it can no longer directly inherit the Thread class.
(Interfaces can implement implements more, but inheritance extends can only inherit singly), and its parent class does not want to inherit Thread because it does not need to implement multithreading
(3) Getting and setting Thread objects
//Get the name of the thread public final String getName() //Set the name of the thread public final void setName(String name)
Set the name of the thread (if no name is set, the default is Thread-? (number)
Method 1: Parametric construction + setXxx (recommended)
//Create objects of MyRunnable class MyRunnable my = new MyRunnable(); //Create an object of the Thread class and pass the object of step C as a construction parameter Thread t1 = new Thread(my); Thread t2 = new Thread(my); t1.setName("User1"); t1.setName("User2"); //Equivalent to the above code Thread t1 = new Thread(my,"User1"); Thread t2 = new Thread(my,"User2");
Method 2: (slightly troublesome, we need to manually write MyThread's parametric construction method, method 1 does not need)
//In the MyThread class public MyThread(String name){ super(name);//Just call the parent class directly } //In the MyThreadTest class MyThread my = new MyThread("admin");
Get the thread name
Note: How to get the thread name in the run method is overridden
//Thread getName() //Runnable //Because the way to implement the interface can't use the method of Thread class directly, but it can be used indirectly. Thread.currentThread().getName()
When implementing the Runnable interface method, note that the main method's test class does not inherit the Thread class, so it is not possible to get the name directly using the getName() method.
//In this case, the Thread class provides a method: //public static Thread currentThread(): //Returns the thread object currently being executed with the return value of Thread, which happens to call the getName() method System.out.println(Thread.currentThread().getName());
(4) Thread scheduling, acquisition and priority setting
If our computer has only one CPU, then the CPU can only execute one instruction at a certain time. Threads can execute instructions only if they get CPU time slices, that is, access rights. So how does Java call threads?
Threads have two scheduling models:
Timesharing Scheduling Model: All threads use CPU in turn and allocate the time slice of CPU occupied by each thread equally.
Preemptive scheduling model: Give priority to high priority threads using CPU. If the threads have the same priority, they will randomly select one. Threads with high priority get more CPU time slices.
Java uses a preemptive scheduling model
//Demonstrates how to set and get thread priority //Returns the priority of the thread object public final int getPriority() //Changing Thread Priority public final void setPriority(int newPriority)
Thread default priority is 5.
Thread priority ranges from 1 to 10.
High thread priority only means that the probability of CPU time slices acquired by threads is high, but the better results can be seen only when the number of times is large or when the threads run many times.
(5) Thread control
Some of them will be used in later cases. These control functions are not very difficult and can be self-tested.
//Thread dormancy public static void sleep(long millis) //Threads join (waiting for the thread to terminate, the main thread to terminate, the other threads start to preempt resources) public final void join() //Thread comity (pausing the currently executing thread object and executing other threads makes the execution of multiple threads more harmonious, but not guaranteeing one person at a time) public static void yield() //Background threads (when one thread ends, other threads end) public final void setDaemon(boolean on) //(Outdated but still usable) public final void stop() //Interrupt thread public void interrupt()
(6) Thread life cycle
New - Create Thread Objects
Ready - Thread objects have been started, but CPU execution has not yet been obtained
Running - Getting CPU Execution
- Blocking - no CPU power, back in place
Death - When the code runs out, the thread dies
(7) Multithread Cinema Ticketing Case
public class SellTickets implements Runnable { private int tickets = 100; @Override public void run() { while (true){ if (tickets > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Selling" + (tickets--) + "Tickets"); } } } }
public class SellTicketsTest { public static void main(String[] args) { //Create resource objects SellTickets st = new SellTickets(); //Creating Thread Objects Thread t1 = new Thread(st, "Window 1"); Thread t2 = new Thread(st, "Window 2"); Thread t3 = new Thread(st, "Window 3"); //Start threads t1.start(); t2.start(); t3.start(); } }
Add a sleep method to the ellTicket class to delay the thread and slow down the execution
By adding delays, several problems arise:
A: The same ticket has been sold many times.
A CPU operation must be atomic (simplest) (duplicated by squeezing two threads in the middle of reading tickets -- the original value and subtracting 1)
B: There were negative votes.
Random and delayed (three threads crammed into a loop at the same time, tickets -- subtraction operation may be executed multiple times in the same cycle and cross the boundary, for example, tickets greater than 0 but crossed the boundary to -1)
That is to say, thread 2 may also be executed while thread 1 executes, instead of thread 2 being unable to execute when thread 1 executes.
Let's first know which problems can lead to problems:
And these reasons are also the criteria for judging whether a program will have thread security problems in the future.
A: Is it a multithreaded environment?
B: Is there shared data?
C: Are there multiple statements that operate to share data?
By contrast, our program does have the above problems, because it satisfies the above conditions.
So how can we solve this problem?
Wrap the code of multiple statements to manipulate shared data into a whole so that when a thread is executed, others can't execute it.
Java provides us with a synchronization mechanism
// Synchronized code block: Synchronized (object){ Synchronized code is required; }
Benefits of synchronization
The emergence of synchronization solves the security problem of multithreading
The Disadvantage of Synchronization
When there are quite a few threads, because each thread will judge the lock on synchronization, which is very resource-consuming and will inevitably reduce the efficiency of the program.
Summary:
A: Who is the lock object of the synchronous code block?
Arbitrary object
B: Synchronization method format and object lock problem?
Adding Synchronization Keyword to Method
Who is the lock object of the synchronization method?
this
C: Static Method and Lock Object Problem?
Who is the lock object of the static method?
Class's bytecode file object.
We use synchronized to improve our program above, the thread security issues ahead.
public class SellTickets implements Runnable { private int tickets = 100; //Create lock objects //Defining this key lock object to the run() method (independent of threads) creates the same lock private Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Selling" + (tickets--) + "Tickets"); } } } } }
(8) Overview and use of lock locks
In order to express more clearly how to lock and release locks, JDK5 later provides a new lock object Lock.
(You can see more clearly where the lock was added and where the lock was released.)
void lock() locking void unlock() release lock
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SellTickets2 implements Runnable { private int tickets = 100; private Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock(); ; if (tickets > 0) { try { Thread.sleep(150); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Selling" + (tickets--) + "Tickets"); } } finally { lock.unlock(); } } } }
(9) Deadlock problem (simple understanding)
Synchronization drawbacks
Low efficiency
If synchronous nesting occurs, deadlock problems are prone to occur.
Deadlock problem
It refers to a phenomenon of waiting for each other when two or more threads compete for resources in the process of execution.
(10) Waiting awakening mechanism
In fact, the cinema scenario we have assumed before has some limitations. The number of tickets we have assumed is certain, but in real life, it is often a coexistence of supply and demand. For example, to buy breakfast, when consumers buy some, the producer's shop will replenish some commodities, in order to study this. In scenarios like this, what we need to learn is Java's Wait-Up mechanism.
Producer-consumer problem, also known as Bounded-buffer problem, is a classic case of multi-process synchronization problem. This problem describes two processes that share fixed size buffers -- the so-called "producer" and "consumer" -- that will occur in real time. The main role of the producer is to generate a certain amount of data into the buffer, and then repeat the process. At the same time, consumers are consuming the data in the buffer. The key to this problem is to ensure that producers do not add data when the buffer is full and consumers do not consume data when the buffer is empty.
Let's explain this in a more popular way.
Java uses a preemptive scheduling model
- A: If consumers get the execution right of CPU first, they will consume data. But now the data is the default value. If it is not meaningful, they should wait for the data to be meaningful before consuming. It's like a buyer who enters a shop early but hasn't made it yet, and can only wait for it to be made before consuming it.
- B: If the producer first grabs the execution power of CPU, it will go back to the production data. But when it generates the data, it will continue to have the execution power. It is unreasonable that it can continue to generate data. You should wait for consumers to consume the data before production. It's like the shop can't make breakfast endlessly, sell some, do it again, and avoid losing money.
To sort out the train of thought:
- A: Producers - first see if there is data, then wait, then produce, and then inform consumers to consume data.
- B: Consumers - first see if there is data, then consume, then wait, and inform producers of production data.
Interpretation: Wake Up - Make Threads in Thread Pool Qualified to Execute
The Object class provides three methods:
//wait for wait() //Wake up a single thread notify() //Wake up all threads notifyAll()
Note: All three methods must be executed in synchronized blocks (e.g. synchronized blocks) and the locks they belong to must be marked when they are used in order to determine which thread on which locks these methods operate.
Why are these methods not defined in the Thread class?
Calls to these methods must be made through lock objects, which we just used are arbitrary lock objects.
Therefore, these methods must be defined in the Object class.
Let's write a simple code implementation waiting for wake-up mechanism
public class Student { String name; int age; boolean flag;// By default, there is no data (false), and if true, there is data. public Student() { } }
public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true){ synchronized (s) { //Judge whether there is data or not //If there is data, wait if (s.flag) { try { s.wait(); //t1 wait, release lock } catch (InterruptedException e) { e.printStackTrace(); } } //Production data without data if (x % 2 == 0) { s.name = "admin"; s.age = 20; } else { s.name = "User"; s.age = 30; } x++; //Now that the data already exists, modify the tag s.flag = true; //Wake-up thread //Wake-up t2, wake-up does not mean that you can execute immediately, you have to rob the CPU of execution. s.notify(); } } } }
package cn.bwh_05_Notify; public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true){ synchronized (s){ //If there is no data, wait if (!s.flag){ try { s.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(s.name + "---" + s.age); //Modify markup s.flag = false; //Wake-up thread t1 s.notify(); } } } }
package cn.bwh_05_Notify; public class StudentTest { public static void main(String[] args) { Student s = new Student(); //Classes set and acquired SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //Thread class Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //Start threads t1.start(); t2.start(); } } //Operating results alternate in turn
Code optimization of waiting wake-up mechanism for producers and consumers
The final version of the code (with major changes in the Student class, then the GetThread class and the SetThread class are much simpler)
public class Student { private String name; private int age; private boolean flag; public synchronized void set(String name, int age) { if (this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.name = name; this.age = age; this.flag = true; this.notify(); } public synchronized void get() { if (!this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(this.name + "---" + this.age); this.flag = false; this.notify(); } }
public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true) { if (x % 2 == 0) { s.set("admin", 20); } else { s.set("User", 30); } x++; } } }
public class GetThread implements Runnable{ private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true){ s.get(); } } }
public class StudentTest { public static void main(String[] args) { Student s = new Student(); //Classes set and acquired SetThread st = new SetThread(s); GetThread gt = new GetThread(s); Thread t1 = new Thread(st); Thread t2 = new Thread(gt); t1.start(); t2.start(); } }
Final Edition Code Features:
- Give Student member variables private.
- Settings and acquisitions are encapsulated as functions and synchronized.
- The only way to set or get a thread is to call a method.
(11) Thread pool
Starting a new thread is expensive because it involves interacting with the operating system. The use of thread pools can improve performance very well, especially when creating a large number of threads with very short lifetime in programs, we should consider using thread pools.
After each thread code in the thread pool ends, it does not die, but goes back to the thread pool and becomes idle again, waiting for the next object to be used.
Before JDK5, we had to implement our own thread pool manually. Starting with JDK5, Java has built-in thread pool support.
JDK5 adds an Executors factory class to generate thread pools in the following ways // Create a thread pool with caching // Caching: Baidu browsed information access again public static ExecutorService newCachedThreadPool() // Create a reusable thread pool with a fixed number of threads public static ExecutorService newFixedThreadPool(intnThreads) // Create a pool of threads with only one thread. The parameter of the previous method is 1. public static ExecutorService newSingleThreadExecutor() The return value of these methods is an ExecutorService object, which represents a thread pool and can execute threads represented by a Runnable object or a Callable object. It provides the following methods Future<?> submit(Runnable task) <T> Future<T> submit(Callable<T> task)
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorDemo { public static void main(String[] args) { //Create a thread pool object that controls how many Thread objects are created ExecutorService pool = Executors.newFixedThreadPool(2); //Threads represented by Runnalble objects or Callable objects can be executed pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); //End thread pool pool.shutdown(); } }
(12) Implementing multithreaded programs by anonymous internal classes
Formats of anonymous inner classes:
new class name or interface name (){ Rewriting method; };
Essence: Subclass objects of this class or interface
public class ThreadDemo { public static void main(String[] args) { new Thread() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + i); } } }.start(); } }
public class RunnableDemo { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + i); } } }).start(); } }
(13) Timer
Timer is a widely used thread tool, which can be used to schedule multiple timing tasks in the way of background threads. In Java, scheduling can be defined through Timer and TimerTask classes
Timer
·public Timer() public void schedule(TimerTask task, long delay) public void schedule(TimerTask task,long delay,long period)
TimerTask
abstract void run() public boolean cancel()
Developing
Quartz is an open source scheduling framework written entirely in java
Ending:
If there are any deficiencies or errors in the content, please leave a message for me, crab and crab! ^^
If you can help me, then pay attention to me! (All articles in this series will be updated at the first time on the Public Number.)
We don't know each other here, but we are all working hard for our dreams.
A public name that insists on pushing original Java technology: more than 20 years of ideal