Java. Net has been introduced before Lang package. Today, we will add two common API s: Java Lang. throwable and Java lang.Thread
1, Exception (java.lang.Throwable)
1. What is abnormal:
Exceptions in Java refer to abnormal conditions that occur during the execution of the program, which will eventually lead to abnormal stop of the JVM.
In object-oriented programming languages such as Java, the exception itself is a class. Generating an exception is to create an exception object and throw an exception object. The way Java handles exceptions is interrupt handling.
The exception does not refer to a syntax error. If the syntax is wrong, the compilation fails, no bytecode file will be generated, and it cannot run at all
2. Classification of anomalies
The exception mechanism actually helps us find problems in the program. The root class of the exception is Java Lang. throwable, which has two subclasses: Java Lang. error and Java Lang. exception, the usual exception refers to Java lang.Exception .
Throwable system:
- Error: a serious error. An error that cannot be handled can only be avoided in advance, like a terminal disease.
- Exception: indicates an exception. After an exception is generated, the programmer can correct it through code to make the program continue to run. It must be handled. Like cold, appendicitis.
Common methods in Throwable:
- public void printStackTrace(): print the details of the exception.
It includes the type of exception, the reason for the exception, and the location of the exception. printStackTrace must be used in the development and debugging stages. - public String getMessage(): get the reason for the exception. When prompted to the user, the error reason will be prompted.
- public String toString(): get the exception type and exception description information (not required).
3.Exception classification
We usually refer to exceptions, because once such exceptions occur, we need to correct the code and repair the program.
Classification of exceptions: check exceptions at compile time or run time?
- Compile time exception: checked exception. At compile time, it is checked. If the exception is not handled, the compilation fails. (such as abnormal date formatting)
- Runtime exception: runtime exception. During runtime, check for exceptions At compile time, run exceptions are not detected by the compiler (no errors are reported). (e.g. mathematical abnormality)
4. Analysis of abnormal generation process
First run the following program, which will generate an array index out of bounds exception ArrayIndexOfBoundsException. We use diagrams to analyze the process of exception generation.
Tools:
public class ArrayTools { // Gets the elements of a given array through the given subscript. public static int getElement(int[] arr, int index) { int element = arr[index]; return element; } }
Test class:
public class ExceptionDemo { public static void main(String[] args) { int[] arr = { 34, 12, 67 }; intnum = ArrayTools.getElement(arr, 4) System.out.println("num=" + num); System.out.println("over"); } }
Diagram of the execution process of the above procedures:
5. Exception handling
Five keywords for Java exception handling: try, catch, finally, throw, throws
(1) throw exception
When writing a program, we must consider the problem of the program. For example, when defining a method, the method needs to accept parameters. Then, when calling the method to use the received parameters, you first need to judge the validity of the parameter data. If the data is illegal, you should tell the caller to pass in the legal data. In this case, you need to tell the caller by throwing an exception.
In java, a throw keyword is provided, which is used to throw a specified exception object. So, how to throw an exception?
- Create an exception object. Encapsulate some prompt information (the information can be written by yourself).
- This exception object needs to be notified to the caller. How to tell? How do I pass this exception object to the caller? This can be done by using the keyword throw. Throw exception object.
Throw is used in a method to throw an exception object, pass the exception object to the caller, and end the execution of the current method.
Use format:
throw new exception class name (parameter);
For example:
throw new NullPointerException("To access arr Array does not exist"); throw new ArrayIndexOutOfBoundsException("The index does not exist in the array and is out of range");
After learning the format of throwing exceptions, we demonstrate the use of throw through the following program.
public class ArrayThread { public static void main(String[] args) { //Create an array int[] arr = {2,4,52,2}; //Find the corresponding element according to the index int index = 4; int element = getElement(arr, index); System.out.println(element); System.out.println("over"); } /* * Find the corresponding element in the array according to the index */ public static int getElement(int[] arr,int index){ //Judge whether the index is out of bounds if(index<0 || index > arr.length - 1){ /* If the judgment conditions are met, the method cannot continue operation after throw ing the exception object. This will end the execution of the current method and notify the caller of the exception. At this time, it needs to be solved through exceptions. */ throw new ArrayIndexOutOfBoundsException("Man, the corner marker is out of bounds~~~"); } int element = arr[index]; return element; } }
Note: if a problem occurs, we will throw out the problem description class, that is, the exception, that is, return the problem to the call of the method
Person.
So what should the caller do? One is to capture, the other is to continue to declare the problem and use the throws declaration.
Objects non null judgment
It consists of some static and practical methods. These methods are null save (null pointer full) or null tolerance (null pointer tolerant). In its source code, throw an exception to the null value of the object.
- Public static < T > t requirenonnull (t obj): check that the specified reference object is not null.
public static T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
(2) declare abnormal throws
Declare exception: identify the problem and report it to the caller. If a compile time exception is thrown through throw in the method without capture and processing (this method will be explained later), it must be declared through throws for the caller to handle.
The keyword throws is used on the method declaration to indicate that the current method does not handle exceptions, but reminds the caller of the method to handle exceptions (throw exceptions).
Declaration exception format:
Modifier return value type method name (parameter) throws exception class name 1, exception class name 2... {}
Code demonstration for declaring exceptions:
public class ThrowsTest { public static void main(String[] args) throws FileNotFoundException { read("a.txt"); } // If a problem occurs when defining a function, it needs to be reported to the caller. This can be declared by using the throws keyword on the method private static void read(String path) throws FileNotFoundException { if (!path.equals("a.txt")) {//If it's not a.txt // I assume that if a.txt does not think the file does not exist, it is an error, that is, an exception throw throw new FileNotFoundException("file does not exist"); } } }
Throws is used to declare exception classes. If multiple exceptions may occur in this method, multiple exception classes can be written after throws, separated by commas.
public class ThrowsTest { public static void main(String[] args) throws IOException { read("a.txt"); } // If a problem occurs when defining a function, it needs to be reported to the caller. This can be declared by using the throws keyword on the method private static void read(String path) throws FileNotFoundException,IOException { if (!path.equals("a.txt")) {//If it's not a.txt // I assume that if a.txt does not think the file does not exist, it is an error, that is, an exception throw throw new FileNotFoundException("file does not exist"); } if (!path.equals("b.txt")) { throw new IOException(); } } }
(3) catch exception try... Catch
If an exception occurs, the program will be terminated immediately, so we have to deal with the exception:
- The method does not handle, but declares throws, which are handled by the caller of the method.
- Use the try catch statement block in the method to handle exceptions.
The way to try catch is to catch exceptions.
Catch exception: in Java, targeted statements for exceptions are caught, and exceptions can be handled in a specified way.
The syntax for catching exceptions is as follows:
try{
write code that may cause exceptions
}Catch (exception type e){
code for handling exceptions
/ / log / print exception information / continue to throw exceptions
}
- try: write code that may cause exceptions in this code block.
- Catch: it is used to catch certain exceptions and handle the caught exceptions.
The code is demonstrated as follows:
public class TryAndCatchTest { public static void main(String[] args) { try { // When an exception occurs, there must be a handling method. Either capture or declare. read("a.txt"); } catch (FileNotFoundException e) { //What exception is thrown in try, and what exception type is defined in parentheses e.printStackTrace(); } System.out.println("Finish"); } //There are compile time exceptions in our current method private static void read(String path) throws FileNotFoundException { if (!path.equals("a.txt")) {//If it's not a.txt // I assume that if a.txt does not think the file does not exist, it is an error, that is, an exception throw throw new FileNotFoundException("file does not exist"); } } }
How to get exception information:
Some viewing methods are defined in the Throwable class:
- public String getMessage(): get the description of the exception. When the user is prompted, the error reason will be prompted.
- public String toString(): get the exception type and exception description information (not required).
- public void printStackTrace(): print the abnormal tracking stack information and output it to the console.
It includes the type of exception, the reason for the exception, and the location of the exception. printStackTrace must be used in the development and debugging stages.
(4) finally code block
Finally: there are some specific codes that need to be executed whether an exception occurs or not. In addition, because the exception will cause program jump, some statements cannot be executed. Finally solves this problem. The code stored in the finally code block will be executed.
When must the code be finally executed?
When we open some physical resources (disk file / network connection / database connection, etc.) in the try statement block, we have to finally close the open resources after using them.
finally syntax:
try... catch... Finally: you need to handle exceptions and finally close resources.
finally, the code reference is as follows:
public class FinallyTest { public static void main(String[] args) { try { read("a.txt"); } catch (FileNotFoundException e) { //What is captured is the compile time exception, and what is thrown is the run time exception throw new RuntimeException(e); }finally { System.out.println("No matter what the program is, it will be executed here."); } System.out.println("Finish"); } //There are compile time exceptions in our current method private static void read(String path) throws FileNotFoundException { if (!path.equals("a.txt")) {//If it's not a.txt // I assume that if a.txt does not think the file does not exist, it is an error, that is, an exception throw throw new FileNotFoundException("file does not exist"); } } }
When try or catch is called to exit the relevant methods of JVM, finally will not execute at this time, otherwise finally will always execute.
Abnormal precautions
-
How to deal with multiple exceptions using capture?
a. Multiple exceptions are handled separately.
b. Multiple exceptions are captured once and processed multiple times.
c. Multiple exceptions are captured and processed at a time.
Generally, we use the method of one capture and multiple processing. The format is as follows:try{ Write code that may cause exceptions }catch(Exception type A e){ When try Appear in A Type exception,Just use it catch To capture. Code to handle exceptions //Log / print exception information / continue to throw exception }catch(Exception type B e){ When try Appear in B Type exception,Just use it catch To capture. Code to handle exceptions //Log / print exception information / continue to throw exception }
Note: this exception handling method requires that the exceptions in multiple catches cannot be the same, and if there is a relationship between child and parent exceptions in multiple catches, the child exceptions shall be handled in the above catch, and the parent exceptions shall be handled in the following catch
-
The runtime exception is thrown and can not be handled. Neither capture nor declare throw.
-
If finally has a return statement, always return the result in finally to avoid this situation
-
If the parent class throws multiple exceptions, when overriding the parent class method, the child class throws the same exception as the parent class, or the child of the parent class exception, or does not throw an exception
-
The parent class method does not throw an exception, and the child class cannot throw an exception when overriding the parent class method. At this time, the subclass generates the exception, which can only be caught and processed, and cannot be declared and thrown
6. Custom exception
(1) why do you need to customize exception classes
In the above code, it is found that these exceptions are internally defined in JDK, but many exceptions will occur in actual development. These exceptions are likely not defined in JDK, such as negative age and negative test scores.
In development, you define exception classes according to your business exceptions. For example, customize a business logic exception: RegisterException, a registered exception class.
How to define exception classes:
- Customize a compile time exception: customize the class and inherit from Java lang.Exception.
- Customize a runtime exception class: customize the class and inherit it from Java lang.RuntimeException .
(2) practice of customizing exceptions
Requirements: we simulate the registration operation. If the user name already exists, we throw an exception and prompt: pro, the user name has been registered.
-
First, define a login exception class RegisterException:
public class RegisterException extends Exception { /** * Empty parameter structure */ public RegisterException() { } /** * @param message Indicates an exception prompt */ public RegisterException(String message) { super(message); } }
Simulate the login operation, use the array to simulate the data stored in the database, and provide the method to judge whether the current registered account exists.
public class RegisterExcetionTest { // The account already exists in the simulation database private static String[] names = {"bill","hill","jill"}; public static void main(String[] args) { //Call method try{ // Possible exception codes checkUsername("bill"); System.out.println("login was successful");//If there is no exception, the registration is successful }catch(RegisterException e){ //Handling exceptions e.printStackTrace(); } } //Judge whether the current registered account exists //Because it is a compile time exception and wants the caller to handle it, declare the exception private static boolean checkUsername(String uname) throws RegisterException { for (String name : names) { if(name.equals(uname)){//If the name is in it, a login exception will be thrown throw new RegisterException("dear"+name+"Already registered!"); } } return true; } }
result
2, Multithreading (java.lang.Thread)
1. The concept of concurrency and parallelism
- Concurrency: two or more events occur in the same time period.
- Parallel: two or more events occur at the same time (at the same time).
In the operating system, multiple programs are installed. Concurrency refers to that multiple programs run at the same time in a period of time. In a single CPU system, only one program can be executed at each time, that is, these programs run alternately in time-sharing, which just gives people the feeling that they run at the same time, because the time-sharing alternate running time is very short.
In multiple CPU systems, these programs that can be executed concurrently can be allocated to multiple processors (CPUs) to realize multi task parallel execution, that is, each processor is used to process a program that can be executed concurrently, so that multiple programs can be executed at the same time. At present, the multi-core CPU in the computer market is multi-core processor. The more cores, the more parallel processing programs, can greatly improve the efficiency of computer operation.
Note: a computer with a single core processor certainly cannot process multiple tasks in parallel. It can only run multiple tasks concurrently on a single CPU. Similarly, threads are the same. From a macro perspective, threads run in parallel, but from a micro perspective, they run in serial, that is, one thread runs one thread. When the system has only one CPU, threads will execute multiple threads in a certain order. We call this situation thread scheduling.
2. Threads and processes
- Process: refers to an application running in memory. Each process has an independent memory space. An application can run multiple processes at the same time; Process is also an execution process of the program and the basic unit of the system running program; A system running a program is a process from creation, running to extinction.
- Thread: a thread is an execution unit in a process. It is responsible for the execution of programs in the current process. There is at least one thread in a process. There can be multiple threads in a process, and this application can also be called a multithreaded program.
In short: after a program runs, there is at least one process, and a process can contain multiple threads
Process:
thread
Thread scheduling:
- Time sharing scheduling
All threads use the right to use the CPU in turn, and allocate the CPU time of each thread equally. - preemptive scheduling
Give priority to the threads with high priority to use CPU. If the threads have the same priority, one will be selected randomly (thread randomness). Java uses preemptive scheduling.
Set thread priority
Detailed explanation of preemptive scheduling:
Most operating systems support concurrent running of multiple processes, and almost all current operating systems support running multiple programs at the same time. For example, now we usually use the editor while using the screen recording software. At the same time, we also open the drawing board, dos window and other software. At this time, these programs are running at the same time, ”It seems that these software are running at the same time. In fact, the CPU (central processing unit) uses the preemptive scheduling mode to switch between multiple threads at a high speed. For a CPU core, only one thread can be executed at a certain time, and the switching speed of the CPU between multiple threads is faster than our feeling, which seems to be running at the same time.
In fact, multithreaded programs can not improve the running speed of programs, but can improve the running efficiency of programs and make the CPU utilization higher.
3. Create thread class
There are two ways to create threads: one is to inherit the Thread class, and the other is to implement the Runnable interface
Java uses Java Lang. Thread class represents threads. All Thread objects must be instances of Thread class or its subclasses. The role of each Thread is to complete a certain task. In fact, it is to execute a program flow, that is, a piece of code executed in sequence. Java uses Thread executors to represent this program flow.
The steps to create and start a multithread by inheriting the Thread class in Java are as follows:
- a. Define a subclass of the Thread class and override the run() method of the class. The method body of the run() method represents the tasks that the Thread needs to complete. Therefore, the run() method is called the Thread execution body.
- b. Create an instance of Thread subclass, that is, create a Thread object
- c. Call the start() method of the thread object to start the thread
Custom thread class:
public class MyThread extends Thread { //Defines the constructor for the specified thread name public MyThread(String name) { //Call the constructor of the String parameter of the parent class to specify the name of the thread super(name); } /** * Rewrite the run method to complete the logic executed by the thread */ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(getName()+i); } } }
Test class:
public class ThreadTest { public static void main(String[] args) { System.out.println("Here is main thread "); //Create custom thread object MyThread mt = new MyThread("cockroach"); //Open new thread mt.start(); //Execute a for loop in the main method for (int i = 0; i < 20; i++) { System.out.println("Wangcai!"+i); } } }
flow chart:
When the program starts and runs main, the java virtual machine starts a process, and the main thread main is created when main() is called. With the start method of the object calling mt, another new thread is started, so that the whole application runs under multithreading.
The difference between calling the start() method and calling the run() method:
How does multithreading run in memory? The above procedures are illustrated as an example:
During multi-threaded execution, in stack memory, in fact, each execution thread has its own stack memory space. Press stack and spring stack of method.
When the task of executing the thread ends, the thread is automatically released in the stack memory. But when all the execution threads end, the process ends.
4. Thread class
Construction method:
- public Thread(): allocate a new thread object.
- public Thread(String name): assign a new thread object with a specified name.
- public Thread(Runnable target): allocate a new thread object with the specified target.
- public Thread(Runnable target,String name): allocate a new thread object with the specified target and specify the name.
Common methods:
- public String getName(): get the name of the current thread.
- public void start(): causes this thread to start executing; The Java virtual machine calls the run method of this thread.
- public void run(): the task to be executed by this thread defines the code here.
- Public static void sleep (long miles): pauses (temporarily stops) the currently executing thread for the specified number of milliseconds.
- public static Thread currentThread(): returns a reference to the currently executing thread object.
5. Create threads by implementing Runnable interface
Using Java Lang. runnable is also a very common method. We only need to rewrite the run method.
The steps are as follows:
- a. Define the implementation class of the Runnable interface and override the run() method of the interface. The method body of the run() method is also the thread execution body of the thread.
- b. Create an instance of the Runnable implementation class and use this instance as the target of the Thread to create the Thread object, which is the real Thread object.
- c. Call the start() method of the thread object to start the thread.
The code is as follows:
//Implement Runnable interface public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } } }
Test class:
public class RunnableTest { public static void main(String[] args) { //Create custom class object thread task object MyRunnable mr = new MyRunnable(); //Create thread object Thread t = new Thread(mr, "cockroach"); t.start(); //Main thread code for (int i = 0; i < 20; i++) { System.out.println("Wangcai " + i); } } }
6. Difference between thread and Runnable
Advantages of implementing Runnable interface over inheriting Thread class:
-
It can avoid the limitation of singleton inheritance in java.
-
Increase the robustness of the program and realize the decoupling operation (separate the start thread task from the set thread task).
public class RunnableTest { public static void main(String[] args) { //The thread task is set in the MyRunnable class MyRunnable mr = new MyRunnable(); //Create thread object //Thread t = new Thread(mr, "Xiaoqiang"); //Thread is used to start the thread task. If you want to change the thread task, you can directly change the parameters passed by thread without changing the thread object Thread t = new Thread(new MyRunnable2(), "Daqiang"); t.start(); for (int i = 0; i < 20; i++) { System.out.println("Wangcai " + i); } } }
-
The Thread pool can only put threads that implement Runable or Callable classes, not directly into classes that inherit threads.
Supplement: in Java, at least 2 threads are started each time the program runs. One is the main thread and the other is the garbage collection thread. Because every time a class is executed using java commands, a JVM is actually started, and each JVM actually starts a process in the operating system.
7. Create threads by anonymous inner classes
Using the anonymous internal class method of threads, it is convenient for each thread to perform different thread task operations.
Implement the Runnable interface by using anonymous internal classes, and re the run method in the Runnable interface:
public class RunnableTest { public static void main(String[] args) { new Thread(new MyRunnable(){ @Override public void run() { //Sub thread code for (int i = 0; i < 20; i++) { System.out.println("Er Gouzi:"+i); } } }, "Daqiang").start(); //Main thread code for (int i = 0; i < 20; i++) { System.out.println("Wangcai " + i); } } }
8. Thread safety
(1) thread safety
Demonstrate thread safety issues:
The cinema sells tickets. We simulate the ticket selling process of the cinema. Assuming that the movie to be played is "huluwa vs. Altman", there are 100 seats in this movie (this movie can only sell 100 tickets).
Let's simulate the ticket window of the cinema and realize that multiple windows sell tickets for the film "huluwa vs. Altman" at the same time (multiple windows sell these 100 tickets together)
Window: using thread object to simulate;
Ticket: Runnable interface subclass to simulate
Simulation ticket:
public class Ticket implements Runnable { //A total of 100 tickets private int tickets = 100; @Override public void run() { //Operation of selling tickets in each window //The window is always open while (true) { if (tickets > 0) {//There are tickets for sale //Ticket issuing operation //Use sleep to simulate the ticket issuing time try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto‐generated catch block e.printStackTrace(); } //Gets the name of the current thread object String name = Thread.currentThread().getName(); System.out.println(name + "Selling:" + tickets--); } } } }
Test class:
public class TicketsTest { public static void main(String[] args) { //Create thread task object Ticket ticket = new Ticket(); //Create three window objects Thread t1 = new Thread(ticket, "Window 1"); Thread t2 = new Thread(ticket, "Window 2"); Thread t3 = new Thread(ticket, "Window 3"); //Sell tickets at the same time t1.start(); t2.start(); t3.start(); } }
result:
Two problems were found in the program:
- The same number of votes, such as 5, was sold twice.
- Nonexistent votes, such as 0 votes and - 1 votes, do not exist
For this problem, the number of votes in several windows (threads) is out of sync. This problem is called thread insecurity.
Thread safety problems are caused by global variables and static variables. If there are only read operations and no write operations on global variables and static variables in each thread, generally speaking, this global variable is thread safe; If multiple threads execute write operations at the same time, thread synchronization generally needs to be considered, otherwise thread safety may be affected.
(2) thread synchronization
When we use multiple threads to access the same resource, and multiple threads have write operations on the resource, thread safety problems are easy to occur.
To solve the security problem of multi-threaded concurrent access to a resource, that is, to solve the problem of duplicate tickets and non-existent tickets, Java provides a synchronized mechanism to solve it.
Solution:
When window 1 thread enters the operation, window 2 and window 3 threads can only wait outside. When window 1 operation ends, window 1, window 2 and window 3 have the opportunity to enter the code for execution. In other words, when a thread modifies a shared resource, other threads cannot modify the resource. After the modification is completed and synchronized, they can grab CPU resources and complete the corresponding operation, which ensures the synchronization of data and solves the phenomenon of thread insecurity.
In order to ensure that each thread can normally perform atomic operations, Java introduces thread synchronization mechanism. There are three ways to complete synchronization:
- Synchronize code blocks.
- Synchronization method.
- Locking mechanism.
① Synchronous code block
Synchronized code block: the synchronized keyword can be used in a block in a method, indicating that mutually exclusive access is only implemented to the resources of this block.
Format:
Synchronized{
code requiring synchronous operation
}
Synchronous lock:
The synchronization lock of an object is just a concept, which can be imagined as marking a lock on the object
- The lock object can be of any type.
- Multiple thread objects should use the same lock
Note: at most one thread is allowed to have a synchronization lock at any time. Whoever gets the lock will enter the code block, and other threads can only wait outside (BLOCKED).
Use synchronized code blocks to resolve Code:
public class Ticket implements Runnable { private int ticket = 100; //Shared object Object lock = new Object(); /* * Execute ticket selling operation */ @Override public void run() { //Operation of selling tickets in each window //The window is always open while (true) { synchronized (lock) { if (ticket > 0) {//There are tickets for sale //Ticket issuing operation //Use sleep to simulate the ticket issuing time try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto‐generated catch block e.printStackTrace(); } //Gets the name of the current thread object String name = Thread.currentThread().getName(); System.out.println(name + "Selling:" + ticket --); } } } } }
When the synchronous code block is used, the above thread safety problem is solved.
Principle of synchronization:
② Synchronization method
Synchronization method: the method modified with synchronized is called synchronization method, which ensures that when thread A executes this method, other threads can only wait outside the method.
Format:
public synchronized void method(){
code that may cause thread safety problems
}
Who is the synchronization lock?
- For non static methods, the synchronization lock is this.
- For static methods, we use the bytecode object (class name. class) of the class where the current method is located.
The synchronization method code is as follows:
public class Ticket implements Runnable { private int ticket = 100; //Shared object /* * Execute ticket selling operation */ @Override public void run() { //Operation of selling tickets in each window //The window is always open while (true) { sellTicket(); } } /* * The lock object is the one who calls this method * The implicit lock object is this * */ public synchronized void sellTicket(){ if (ticket > 0) {//There are tickets for sale //Ticket issuing operation //Use sleep to simulate the ticket issuing time try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto‐generated catch block e.printStackTrace(); } //Gets the name of the current thread object String name = Thread.currentThread().getName(); System.out.println(name + "Selling:" + ticket --); } } }
③ Lock lock
java.util.concurrent.locks.Lock mechanism provides a wider range of locking operations than synchronized code blocks and synchronized methods. Synchronized code blocks / synchronized methods have the functions of lock. In addition, lock is more powerful and object-oriented.
Lock lock is also known as synchronous lock. The locking and releasing methods are as follows:
- public void lock(): add synchronization lock.
- public void unlock(): release the synchronization lock.
public class Ticket implements Runnable { private int ticket = 100; //Create Lock object Lock lock = new ReentrantLock(); /* * Execute ticket selling operation */ @Override public void run() { //Operation of selling tickets in each window //The window is always open while (true) { lock.lock(); if (ticket > 0) {//There are tickets for sale //Ticket issuing operation //Use sleep to simulate the ticket issuing time try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto‐generated catch block e.printStackTrace(); } //Gets the name of the current thread object String name = Thread.currentThread().getName(); System.out.println(name + "Selling:" + ticket--); } lock.unlock(); } } }
9. Thread status
(1) overview of thread status
When a thread is created and started, it neither enters the execution state as soon as it is started, nor is it always in the execution state. In the life cycle of a thread,
How many states are there? In the API, Java lang.Thread. State this enumeration gives six thread states:
The conditions for the occurrence of each thread state are listed here. Each state will be analyzed in detail below
① Timed Waiting
Implement a counter, count to 100, pause for 1 second between each number, and output a string every 10 numbers
public class TimeWaitTest extends Thread{ public void run() { for (int i = 0; i < 100; i++) { if ((i) % 10 == 0) { System.out.println("‐‐‐‐‐‐‐" + i); } System.out.print(i); try { Thread.sleep(1000); System.out.print(" Thread sleep for 1 second!\n"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { System.out.println("Why don't you go"); new TimeWaitTest().start(); } }
Note: the time specified in sleep() is the shortest time that the thread will not run. Therefore, the sleep() method can not guarantee that the thread will start executing immediately after the sleep expires (it has something to do with computer performance and other factors).
Timed Waiting thread state diagram:
② BLOCKED
The Blocked state is described in the API as: a thread that is blocking and waiting for a monitor lock (lock object) is in this state.
We have learned the synchronization mechanism, so this state is very easy to understand. For example, the same lock is used in the code of thread A and thread B. if thread A obtains the lock and thread A enters the Runnable state, thread B enters the Blocked lock blocking state.
This is from the Runnable state to the Blocked state. In addition, the Waiting and Time Waiting States will also enter the blocking state in some cases, and this part will lead you to understand as an expanded knowledge point.
③ Waiting (unlimited waiting)
The watching state is described in the API as: a thread that is waiting for another thread to perform a special (wake-up) action indefinitely is in this state.
The code is as follows:
public class TimeWaitTest extends Thread{ private static Object obj = new Object(); public static void main(String[] args) { //1. Create a waiting thread // Demonstrate waiting new Thread(new Runnable() { @Override public void run() { while(true){ synchronized (obj){ try { System.out.println( Thread.currentThread().getName() +"=== Get the lock object, call wait Method, enter waiting Status, releasing lock object"); obj.wait();//Enter the Waiting infinite wait state and release the lock //obj.wait(5000); // Wait for 5 seconds and wake up automatically } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( Thread.currentThread().getName() + "=== from waiting The state wakes up, obtains the lock object, and continues to execute"); } } } },"Wait thread").start(); //2. Create a wake-up thread. The object waiting for the call of wait() should be consistent with the object called by notify() new Thread(new Runnable() { @Override public void run() { while (true){ //Wake up every 3 seconds try { System.out.println( Thread.currentThread().getName() +"‐‐‐‐‐ Wait 3 seconds"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj){ System.out.println( Thread.currentThread().getName() +"‐‐‐‐‐ Get lock object,call notify Method to release the lock object"); obj.notify();//After obtaining the lock object, call the notify method to release the lock object } } } },"Wake up thread").start(); } }
Through the above case, we will find that an object that calls an object The thread of the wait method will wait for another thread to call the object of this object Notify() method or object Notifyall() method.
In fact, the waiting state is not the operation of a thread. It reflects the communication between multiple threads. It can be understood as the cooperative relationship between multiple threads. Multiple threads will strive for locks, and there is a cooperative relationship between them. Just like you and your colleagues in the company, you may have competition for promotion, but more often you work together to complete some tasks.
When multiple threads cooperate, such as A or B thread, if the A thread calls the wait() method in the Runnable (runtime) state, then the A thread enters Waiting (infinite wait) state, and at the same time loses the synchronization lock. If this time the B thread gets the synchronization lock and invokes the notify() method in the running state, it wakes the A thread with infinite wait. Note that if the lock object is obtained, thread a will enter the runnable state after waking up; If the lock object is not obtained, it enters the Blocked state.
Waiting thread state diagram
(2) supplementary knowledge points
An interesting tip:
When reading the API, we will find that timed Waiting is closely related to the Waiting state. For example, the wait method in the Waiting state is null, while the wait method in timed Waiting is with parameters. This method with parameters is actually a countdown operation, which is equivalent to a small alarm clock in our life. We set the time and notify it when it comes. However, if we get (wake up) notification in advance, it will be superfluous to set the time in the notification, so this design scheme is actually killing two birds with one stone. If there is no wake-up notification, the thread will be in the timed Waiting state until the countdown is completed and will wake up automatically; If it is notified during the countdown, the thread wakes up immediately from the timed Waiting state.
10. Wait for wake-up mechanism
(1) inter thread communication
Concept: multiple threads are processing the same resource, but the processing actions (thread tasks) are different.
For example, thread A is used to generate steamed stuffed buns and thread B is used to eat steamed stuffed buns. Steamed stuffed buns can be understood as the same resource. The actions processed by thread A and thread B are production and consumption, so there is A thread communication problem between thread A and thread B.
Why handle inter thread communication:
When multiple threads execute concurrently, by default, the CPU switches threads randomly. When we need multiple threads to complete a task together, and we want them to execute regularly, some coordinated communication is needed between multiple threads to help us operate a piece of data together.
How to ensure inter thread communication and make effective use of resources:
When multiple threads are processing the same resource and different tasks, thread communication is needed to help solve the use or operation of the same variable between threads. That is, multiple threads avoid contention for the same shared variable when operating the same data. That is, we need to use certain means to make each thread make effective use of resources. This means is the waiting wake-up mechanism.
(2) wait for wake-up mechanism
What is the waiting wake-up mechanism?
This is a collaboration mechanism between multiple threads. When it comes to threads, we often think of the race between threads, such as competing for locks, but this is not the whole story. There will also be a cooperation mechanism between threads. Just like you and your colleagues in the company, you may have competition for promotion, but more often
I hope you will work together more to complete some tasks.
After a thread performs a specified operation, it enters the wait state (wait()) and waits for other threads to wake up after executing their specified code (notify()); When there are multiple threads waiting, you can use notifyAll() to wake up all waiting threads if necessary.
wait/notify is a cooperative mechanism between threads.
Methods in waiting for wake-up
The wake-up waiting mechanism is used to solve the problem of inter thread communication. The meanings of the three methods used are as follows:
- Wait: the thread is no longer active, no longer participates in scheduling, and enters the wait set. Therefore, CPU resources will not be wasted, nor will it compete for locks. At this time, the thread state is WAITING. It also waits for other threads to perform a special action, that is, to "notify" the threads WAITING on this object to be released from the wait set and re-enter the ready queue
- notify: select a thread in the wait set of the notified object to release; For example, when a restaurant has a free seat, the customer waiting for the longest meal takes the first seat.
- notifyAll: release all threads on the wait set of the notified object.
Note: even if only one waiting thread is notified, the notified thread cannot resume execution immediately, because the place where it was interrupted was in the synchronization block, and now it does not hold the lock, so she needs to try to obtain the lock again (it is likely to face competition from other threads). After success, she can resume execution at the place where the wait method was called.
The summary is as follows:
- If the lock can be obtained, the thread will change from WAITING state to RUNNABLE state;
- Otherwise, when it comes out of the wait set and enters the entryset, the thread will change from the WAITING state to the BLOCKED state
Details of calling wait and notify methods
- The wait method and notify method must be called by the same lock object. Because: the corresponding lock object can wake up the thread after using the wait method called by the same lock object through notify.
- The wait method and notify method are methods belonging to the Object class. Because: the lock Object can be any Object, and the class of any Object inherits the Object class.
- The wait method and notify method must be used in the synchronization code block or synchronization function. Because: these two methods must be called through the lock object.
producer consumer problem
The waiting wake-up mechanism is actually a classic problem of "producers and consumers".
Take the production of steamed stuffed buns and the consumption of steamed stuffed buns as an example. How can the waiting wake-up mechanism make effective use of resources
Baozi shop thread produces baozi, and eating thread consumes baozi. When there are no steamed stuffed buns (the state of steamed stuffed buns is false), the eating thread waits, and the steamed stuffed buns shop thread produces steamed stuffed buns (that is, the state of steamed stuffed buns is true), and notifies the eating thread (canceling the waiting state of eating). Because there are already steamed stuffed buns, the steamed stuffed buns shop thread enters the waiting state. Next, whether the eating thread can execute further depends on the acquisition of the lock. If the food obtains the lock, it will execute the action of eating steamed stuffed buns, eat the steamed stuffed buns (the status of steamed stuffed buns is false), notify the thread of the steamed stuffed buns shop (release the waiting state of the steamed stuffed buns shop), and the food thread enters the waiting state. Whether the package shop thread can execute further depends on the acquisition of the lock.
Code demonstration:
-
Package resources:
public class Baozi { String pier ; String xianer ; boolean flag = false ;//Does the package sub resource have a package sub resource status ... }
-
Package thread class:
public class BaoZiPu extends Thread { private Baozi bz; //Structural method with parameters public BaoZiPu(String name, Baozi bz) { super(name); this.bz = bz; } @Override public void run() { //Record the number of steamed buns int count = 0; //Steamed stuffed bun while (true) { //Synchronization. Only when the thread lock of the public resource is obtained can the thread be in the RUNNABLE state synchronized (bz) { if (bz.flag == true) {//Package resource exists try { bz.wait();//If the package sub resource exists, let the package sub store thread enter the Waiting state } catch (InterruptedException e) { e.printStackTrace(); } } // There are no steamed buns to make steamed buns System.out.println("The steamed stuffed bun shop began to make steamed stuffed buns"); if (count % 2 == 0) { // Ice skin five kernel bz.pier = "Ice skin"; bz.xianer = "Wuren"; } else { // Thin skinned beef and scallion bz.pier = "Thin skin"; bz.xianer = "Beef and scallion"; } count++; bz.flag = true; System.out.println("The steamed stuffed bun is made:" + bz.pier + bz.xianer); System.out.println("Eat, eat"); //Wake up waiting thread (eating) bz.notify(); } } } }
-
Feeding thread class:
public class Chihuo extends Thread { private Baozi bz;//Public resource bun //Structural method with parameters public Chihuo(String name,Baozi bz){ super(name); this.bz = bz; } @Override public void run() { while(true){ //Only by getting the thread lock of the public resource can the thread be in the RUNNABLE state synchronized (bz){ if(bz.flag == false){//No steamed stuffed bun try { bz.wait();//Call the wait() method to make the thread enter the WAITING state. At this time, the thread enters the wait set } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("The food is eating"+bz.pier+bz.xianer+"steamed stuffed bun"); bz.flag = false;//After eating steamed stuffed bun, change the state of steamed stuffed bun bz.notify();//Wake up a thread in the bz object wait set } } } }
-
Test class:
public class TestDemo { public static void main(String[] args) { //Waiting for wake-up cases Baozi bz = new Baozi(); Chihuo ch = new Chihuo("foodie",bz); BaoZiPu bzp = new BaoZiPu("Steamed stuffed bun shop",bz); ch.start(); bzp.start(); } }
-
Execution results:
11. Thread pool
When we use threads, we create a thread, which is very easy to implement, but there will be a problem:
If there are a large number of concurrent threads and each thread executes a task for a short time, the efficiency of the system will be greatly reduced because it takes time to create and destroy threads frequently.
So is there a way to make threads reusable, that is, after executing a task, they can continue to execute other tasks without being destroyed?
In Java, this effect can be achieved through thread pool. Let's explain the thread pool of Java in detail.
(1) thread pool concept
Thread pool: in fact, it is a container containing multiple threads, in which threads can be used repeatedly, eliminating the operation of frequently creating thread objects and consuming too many resources without repeatedly creating threads.
Rational utilization of thread pool can bring three benefits:
- Reduce resource consumption. The number of threads created and destroyed is reduced. Each worker thread can be reused and can perform multiple tasks.
- Improve response speed. When the task arrives, the task can be executed immediately without waiting for the thread to be created.
- Improve thread manageability. You can adjust the number of work line threads in the thread pool according to the affordability of the system to prevent the server from getting tired due to excessive memory consumption (each thread needs about 1MB of memory. The more threads are opened, the more memory will be consumed, and finally crash).
(2) use of thread pool
The top-level interface of thread pool in Java is Java util. concurrent. Executor, but strictly speaking, executor is not a thread pool, but just a tool for executing threads. The real thread pool interface is Java util. concurrent. ExecutorService.
Configuring a thread pool is complex, especially when the principle of thread pool is not very clear, it is likely that the configured thread pool is not better, so in Java util. concurrent. Executors thread factory class provides some static factories to generate some common thread pools. Officials recommend using the executors project class to create thread pool objects.
There is a method in the Executors class to create a thread pool, as follows:
- public static ExecutorService newFixedThreadPool(int nThreads): returns the thread pool object. (a bounded process pool is created, that is, the maximum number of threads in the pool can be specified)
If a thread pool ExecutorService object is obtained, how to use it? Here, a method to use the thread pool object is defined as follows:
- public Future<?> Submit (runnable task): get a thread object in the thread pool and execute it
Future interface: used to record the results generated after the execution of thread tasks. Thread pool creation and use.
To use thread objects in the thread pool:
- Create a thread pool object.
- Create a Runnable interface subclass object. (task)
- Submit the Runnable interface subclass object. (take task)
- Close the thread pool (not normally).
The case code is as follows:
/** * Runnable Implementation class code: */ public class MyRunnable implements Runnable{ @Override public void run() { System.out.println("I want a coach"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Here comes the coach: " + Thread.currentThread().getName()); System.out.println("Teach me to swim,After handing it in, the coach returned to the swimming pool"); } }
/** * Test class of thread pool */ public class ThreadPoolTest { public static void main(String[] args) { // Create thread pool object ExecutorService service = Executors.newFixedThreadPool(2);//Contains 2 Thread objects // Create Runnable instance object MyRunnable r = new MyRunnable(); /** * How to create thread objects yourself * Thread t = new Thread(r); * t.start(); //Call run() in MyRunnable */ // Gets the thread object from the thread pool and then calls run() in MyRunnable. service.submit(r); // Get another thread object and call run() in MyRunnable service.submit(r); service.submit(r); // Note: after the submit method is called, the program does not terminate because the thread pool controls the closing of threads. // The used threads are automatically returned to the thread pool //Service shutdown();// Turn off the thread pool (not normally) } }
3, Lambda expression (Java 1.8 new)
1. Redundant Runnable code
When a thread needs to be started to complete a task, it is usually through Java Lang. runnable interface to define the task content, and use Java Lang. thread class to start the thread. The code is as follows:
public class RunnableTest { public static void main(String[] args) { // Anonymous Inner Class new Thread(new Runnable() { @Override public void run() { // Override abstract method System.out.println("Multithreaded task execution!"); } }).start(); // Start thread } }
Code analysis:
For the anonymous inner class usage of Runnable, several points can be analyzed:
- The Thread class requires the Runnable interface as a parameter, and the abstract run method is used to specify the core of the Thread task content;
- In order to specify the method body of run, the implementation class of Runnable interface must be required;
- In order to save the trouble of defining a RunnableImpl implementation class, anonymous inner classes have to be used;
- The abstract run method must be overridden, so the method name, method parameters and method return value must be written again without error;
- In fact, it seems that only the method body is the key.
Experience Lambda's better writing
public class RunnableTest { public static void main(String[] args) { // Anonymous Inner Class new Thread(() -> System.out.println("Multithreaded task execution!")).start(); // Start thread } }
The execution effect of this code is exactly the same as that just now. It can be passed at the compilation level of 1.8 or higher. From the semantics of the code, we can see that we start a thread, and the content of the thread task is specified in a more concise form.
There is no longer the constraint of "having to create interface objects", and there is no longer the burden of "abstract method override", which is so simple!
How does Lambda beat object orientation? In the above example, the core code is just as follows:
() ‐> System. out. Println ("multithreaded task execution!")
semantic analysis
After careful analysis of the semantics in the code, the Runnable interface has only one definition of run method: public abstract void run();
That is, a scheme for doing things is formulated (actually a function):
- No parameter: the scheme can be executed without any conditions.
- No return value: the scheme does not produce any results.
- Code block (method): the specific implementation steps of the scheme.
The same semantics is embodied in Lambda grammar, which should be simpler:
() ‐> System. out. Println ("multithreaded task execution!")
- The first pair of parentheses are the parameters of the run method (none), which means that no conditions are required;
- An arrow in the middle represents passing the previous parameter to the following code;
- The following output statement is the business logic code.
2 Lambda standard format
Lambda omits the object-oriented rules and regulations, and the format consists of three parts:
- Some parameters
- An arrow
- A piece of code
The standard format for Lambda expressions is:
(parameter type parameter name) - > {code statement}
Format Description:
- The syntax in parentheses is consistent with the parameter list of traditional methods: leave blank if there is no parameter; Multiple parameters are separated by commas.
- ->Is a newly introduced syntax format, which represents pointing action.
- The syntax in braces is basically consistent with the requirements of traditional method body.
3. How to write lambda expressions in various formats
① Use Lambda standard format 1 (no parameter, no return)
Given a Cook interface, it contains the only abstract method makeFood, with no parameters and no return value. As follows:
public interface Cook { void makeFood(); }
In the following code, call the makeFood method using the anonymous inner class and Lambda's standard format respectively, and print out "eat!" word:
public class InvokeCookTest { public static void main(String[] args) { //invokeCook method through anonymous inner class invokeCook(new Cook() { @Override public void makeFood() { System.out.println("Anonymous inner class to eat!"); } }); //Invoking the invokeCook method through a Lambda expression invokeCook(()->{System.out.println("lambda ate");}); } //Invoke the makeFood() abstract method in the interface by calling the invokeCook method private static void invokeCook(Cook cook) { cook.makeFood(); } }
② Use Lambda standard format 2 (return with parameters)
Given a Calculator calculator interface with an abstract method calc, you can add two int numbers to get the sum value:
/* Given a Calculator calculator interface with an abstract method calc, you can add two int numbers to get the sum value */ public interface Calculator { //Define a method to calculate the sum of two int integers and return the result public abstract int calc(int a, int b); }
In the following code, please call the invokeCalc method in Lambda's standard format to complete the addition calculation of 120 and 130:
/* Lambda Expressions with parameters and return values Requirements: Given a Calculator calculator interface with an abstract method calc, you can add two int numbers to get the sum value The invokeCalc method is called using Lambda's standard format to complete the addition calculation of 120 and 130 */ public class Demo01Calculator { public static void main(String[] args) { //Call the invokeCalc method. The parameter of the method is an interface. Anonymous inner classes can be used invokeCalc(10, 20, new Calculator() { @Override public int calc(int a, int b) { return a+b; } }); //Simplify writing anonymous inner classes using Lambda expressions invokeCalc(120,130,(int a,int b)->{ return a + b; }); } /* Define a method Parameter passes two integers of type int Parameter passing Calculator interface Method calls the method calc in the Calculator to calculate the sum of two integers */ public static void invokeCalc(int a,int b,Calculator c){ int sum = c.calc(a,b); System.out.println(sum); } }
Parentheses represent the parameters of calc abstract method of Calculator interface, and braces represent the method body of calc
4.Lambda omitted format
Omission rule
On the basis of Lambda standard format, the rules for using ellipsis are as follows:
- The types of parameters in parentheses can be omitted;
- If there is only one parameter in the parenthesis, the parenthesis can be omitted;
- If there is only one statement in braces, you can omit the braces, return keyword and statement semicolon regardless of whether there is a return value or not.
Simplify lambda expressions by omitting:
Given a Calculator calculator interface with an abstract method calc, you can add two int numbers to get the sum value:
/* Given a Calculator calculator interface with an abstract method calc, you can add two int numbers to get the sum value */ public interface Calculator { //Define a method to calculate the sum of two int integers and return the result public abstract int calc(int a, int b); }
In the following code, call the invokeCalc method using Lambda's simplified format to complete the addition calculation:
/* Lambda Expressions with parameters and return values Requirements: Given a Calculator calculator interface with an abstract method calc, you can add two int numbers to get the sum value The invokeCalc method is called using Lambda's standard format to complete the addition calculation of 120 and 130 */ public class Demo01Calculator { public static void main(String[] args) { //Writing of anonymous inner class invokeCalc(120, 130, new Calculator() { @Override public int calc(int a, int b) { return a+b; } }); //Standard writing of lambda invokeCalc(120,120,(int a,int b)->{return a + b;}); //Simplified writing of lambda (omitting parameter types, {}, semicolon and return) invokeCalc(110,110,(a,b)-> a + b); } /* Define a method Parameter passes two integers of type int Parameter passing Calculator interface Method calls the method calc in the Calculator to calculate the sum of two integers */ public static void invokeCalc(int a,int b,Calculator c){ int sum = c.calc(a,b); System.out.println(sum); } }
5 premise of lambda
Lambda's syntax is very concise and completely free from the constraints of object-oriented complexity. However, there are several problems that need special attention when using:
- Lambda must have an interface, and there must be only one abstract method in the interface. Whether it is the built-in Runnable, Comparator interface or user-defined interface of JDK, lambda can be used only when the abstract methods in the interface exist and are unique.
- Using Lambda must have context inference.
That is, the parameter or local variable type of the method must be the interface type corresponding to Lambda, and Lambda can be used as an instance of the interface.