- The difference between synchronous and asynchronous
Synchronization: in the synchronization process of multiple threads, only one thread is working and other threads are waiting. This process is single threaded (serial)
Asynchronous: multiple threads are running at the same time, each doing its own thing (parallel)
8.1. Concept of program, process and thread
- The program is static. Compared with dynamic, static means that it is not loaded into memory, there is no CPU, does not participate in the operation, and is silently stored in the storage space
- Process: when running a program, it needs to be loaded into the memory space, and the CPU needs to allocate computing resources to start computing. It can be understood as a running program
- Thread: a process can be further refined into a thread, which is an execution path within a program
Each thread has its own independent: stack, program counter
Multiple threads share the structure of the same process: method area and heap
- Understanding of single core CPU and multi-core CPU
Single core CPU is actually a fake multithreading, because the CPU can switch quickly and looks like parallel execution
Multi core CPU must be executed by multiple threads at the same time - Understanding of parallelism and concurrency
Parallel: multiple CPU s execute multiple tasks at the same time. For example, multiple people do different things at the same time: multiple basketball courts, each playing
Concurrency: one CPU (using time slice) executes multiple tasks at the same time. For example, second kill, multiple people do the same thing: everyone in a certain court goes to grab a basketball
8.2. Creation and use of threads
- Create multithreading mode 1: inherit Thread class
- Description of two problems during thread creation
The creation of multithreading, method 1: inherit from the Thread class
1. Create a subclass that inherits Thread class
2. Override the run() method of Thread class -- > declare the operation to be executed by this Thread in run()
3. Create objects of subclasses of Thread class
4. Call start() through this object
public class ThreadTest { public static void main(String[] args) { // The main method is what the main thread does // 3. Create objects of subclasses of Thread class MyThread t1 = new MyThread(); // 4. Call start() of thread class through the above object: ① start the current thread ② after starting to the thread, the start method will automatically call run() of the current thread // Before calling start, including calling itself, it is all done by the main thread // At this time, two threads are executing at the same time and interact with each other, t1.start(); // To start a thread, you must call the start method instead of run() // Problem 1: you cannot start a thread by calling run() directly // t1.run(); // If there is no more Thread, it is just a run method that creates an object and calls the Thread class. It only reflects the object calling method and does not open a new Thread at all. After the method is executed, the following logic will be followed, which means that the logic in the called run method is still done in the main Thread // Problem 2: start another thread and traverse an even number within 100. You can't let the thread that has called start() execute. An IllegalThreadStateException will be reported // t1.start(); // An error is reported. A thread can only start once // To create multiple threads, create multiple objects // You need to recreate the object of a thread MyThread t2 = new MyThread(); t2.start(); // The following operations are still performed in the main thread for (int i = 0;i < 100;i++){ if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i + " main()"); } } } } // 1. Create a subclass inherited from Thread class class MyThread extends Thread{ // 2. Override the run method of Thread class @Override public void run() { for (int i = 0;i < 100;i++){ if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } }
- Practice of inheritance method:
Create two sub threads, one of which traverses an even number within 100 and the other traverses an odd number within 100
public class ThreadDemo { public static void main(String[] args) { // Create two subclass objects MyThread1 m1 = new MyThread1(); MyThread2 m2 = new MyThread2(); // The two objects respectively call the start method of Thread class to start the Thread m1.start(); m2.start(); // Anonymous object method for creating anonymous subclasses of Thread class (easy to write) // new Thread().start(); // This is not correct. Calling start here is to call the run in the thread's own class. What needs to be called is the run method rewritten by the new thread new Thread(){ // After rewriting the run method, the new object here is the object of the anonymous subclass of the Thread class. If the subclass has no name, the Thread class is used as the @Override public void run() { for (int i = 0; i < 100; i++){ if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } }.start(); new Thread(){ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } }.start(); } } // Two threads do different things. Create two subclasses of Thread class // Traverse even subclasses class MyThread1 extends Thread{ // Override the run method of the Thread class @Override public void run() { for (int i = 0; i < 100; i++){ if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } } // Traversing subclasses of odd numbers class MyThread2 extends Thread{ // Override the run method of the Thread class @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } }
- Common methods of threading:
Common methods for testing Thread class:
1.start(): start the current thread; Call the run() of the current thread
2.run(): you usually need to override this method in the Thread class and declare the operation to be performed by the created Thread in this method
3. Currentthread(): the static method of thread class, which returns the object of the thread executing the current code. The method definition is to return a current instance
It is equivalent to the set and get methods of the name attribute of the thread
4.getName(): get the name of the current thread
5.setName(): sets the name of the current thread
6.yield(): release the usage right of the current CPU. Of course, it is possible to allocate it to the current thread at the next moment
7.join(): in thread a, calling thread b join(), at this point, thread a will enter the blocking state. CPU wants him to perform and cannot execute. Until the thread b is fully executed, the thread a ends the blocking state. Then we will see when CPU will allocate resources to you, and allocate the resources to the later execution.
8.stop(): obsolete. When this method is executed, the current thread will be forcibly terminated. The thread will enter the end of the life cycle and die directly
9.sleep(long millitime): let the current thread "sleep" for the specified millitime milliseconds. After sleep, wait for the CPU to allocate resources. After allocation, it can be executed. Within the specified millitime milliseconds, the current thread is blocked
10.isAlive(): judge whether the current thread is alive
Thread communication: wait()/ notify()/notifyAlll(): these three methods are defined in the Object class
- Supplement: classification of threads
One is the daemon thread, such as gc() garbage collection thread
One is the user thread, such as the main() main thread. The user thread ends and the daemon thread ends
- Thread scheduling
- Thread priority setting
Thread priority:
- Divided into 10 gears
MAX_PRIORITY: 10
MIN_PRIORITY: 1
NORM_ Priority: 5 -- > Default thread priority - How to get and set the priority of the current thread:
getPriority(): get the priority of the thread
setPriority(int p): sets the priority of the thread
Note: high priority threads should seize the execution right of the CPU of low priority threads. But only in terms of probability, high priority threads will be executed with high probability
This does not mean that low priority threads are executed only after high priority threads are executed
public class ThreadMethodTest { public static void main(String[] args) { // The Thread name provided by default is Thread-0, because the null parameter constructor of the current subclass is called here, and the constructor will call the super() of the parent class. According to the Thread source code, the initial Thread name of the null parameter constructor of Thread class is Thread-0, increasing in turn // The second way to name the thread is through the constructor HelloThread h1 = new HelloThread("Thread:1"); // Method 1 of customizing thread Name: // h1.setName("thread one")// It must be executed before start(), if it is late after the thread starts // Set the priority of each thread, which is also set before the thread starts h1.setPriority(Thread.MAX_PRIORITY); h1.start(); // Name the main thread: the execution is valid only before the execution of getName() Thread.currentThread().setName("Main thread"); // Set main thread priority Thread.currentThread().setPriority(Thread.MIN_PRIORITY); for (int i = 0; i < 100; i++) { if (i % 2 == 0) { // Output the thread name and thread priority of the current thread System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i); } /*if (i == 20) { // The Thread needs to call join(), because join is a method in the Thread class, and the main method is in the public class. This class has no join method, so it directly calls an error // When thread b is called join() in thread a, thread a is blocked until the thread b is fully executed, and the thread a ends the blocking state. try { h1.join(); // When i==20, the main thread will not start until the sub thread is executed } catch (InterruptedException e) { e.printStackTrace(); } }*/ } // System.out.println(h1.isAlive()); // Determine whether the H1 thread is alive. If the thread has not finished executing, it is still alive } } class HelloThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { // sleep(); Thrown exceptions can only be solved with try catch, not throws, because the run method of this class overrides the run method of Thread class, and the run method of Thread class does not throw exceptions // If the method of the parent class has not been thrown, the method overridden by the child class must not be thrown; The exception thrown by the subclass is ≤ the exception of the parent class, so sleep() can only use try catch // Sleep means that once the thread executes the sleep method, it will also block for one second. After one second, it will not output the following information immediately. It will not continue to move forward until the CPU allocates the resources and obtains the resources // Within one second, even if the CPU wants to allocate resources, it can't go down because the thread is blocked /*try { sleep(10); // Force the current thread to block, }catch(InterruptedException e){ e.printStackTrace(); }*/ // Output the thread name and thread priority of the current thread System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i); } /*if (i % 20 == 0) { // The current thread is the object of the current class // yield(); Release the CPU execution right, but it is also possible that the original thread snatches back the CPU usage right this.yield(); // this It is the object of the current class, equivalent to h1, that is, the current Thread.currentThread(),this can be omitted }*/ } } // Name the thread in the subclass through the constructor public HelloThread(String name) { super(name); // Call the parameterized constructor of the Thread class } }
- Example: inherit Thread mode: multi window ticket selling
Example: create three windows to sell tickets, and the total number of tickets is 100
There is a thread safety problem to be solved
public class WindowTest { public static void main(String[] args) { // Create three thread objects Window t1 = new Window(); Window t2 = new Window(); Window t3 = new Window(); t1.setName("Window one"); t2.setName("Window II"); t3.setName("Window three"); t1.start(); t2.start(); t3.start(); } } class Window extends Thread { private static int ticket = 100; // Static properties. There are only 100 tickets for creating several threads, and each object shares the same static variable public Window(){ } // Override the run method of the Thread class @Override public void run() { while (true){ if (ticket > 0){ System.out.println(Thread.currentThread().getName() + " Selling tickets,The ticket number is: " + ticket); ticket--; }else{ break; } } } }
- Create multithreading mode 2: implement Runnable interface
- Create a class that implements the Runnable interface
- The implementation class implements the abstract method in Runnable: run()
- Create an object that implements the class
- Pass this object as a parameter to the constructor of the Thread class to create an object of the Thread class
- start() is called through the object of the Thread class
// 1. Create a class that implements the Runnable interface class MThread implements Runnable{ //2. Implement the class to implement the abstract method in Runnable: run() @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); // getName() cannot be used directly here, because mtthread class does not inherit Thread but Object class, and the implemented interface does not have getName method } } } } public class ThreadTest1 { public static void main(String[] args) { // 3. Create an object that implements the class MThread mthread = new MThread(); // 4. Pass this object as a parameter to the constructor of Thread class and create the object of Thread class Thread t1 = new Thread(mthread);// Polymorphic form: Argument: Runnable target = new mThread(); // 5. Call start() through the Thread class object ① start the Thread; ② Calling the run() -- > of the current Thread calls the run() of the target of Runnable type, and the target is assigned by mthread, so: // According to the source code of the Thread class, one of the constructors of the Thread class has a Runnable target parameter, which is a variable of type Runnable interface defined in the Thread // According to the run() source code rewritten by the Thread class, if the formal parameter target is assigned, the run method of the formal parameter will be called. If it is not assigned, the run() rewritten by the subclass inheriting the Thread will be called, so the run() of mtthread will be called t1.setName("Thread one"); t1.start();// At this time, the thread is t1, and whoever start s the thread will be the one // Start another thread, traverse an even number within 100, and share the same interface implementation class Thread t2 = new Thread(mthread); // Anonymous class t2.setName("Thread two"); t2.start(); // Finally, it will also return to the call of the run method of the interface implementation class } }
- Example: realizing Runnable mode: selling tickets in multiple windows
class Window1 implements Runnable{ // Because only one object of Window1 class is created, the ticket is the same, so static is not used private int ticket = 100; // Override run() @Override public void run() { while (true){ if (ticket > 0){ System.out.println(Thread.currentThread().getName() + ":" + ticket); ticket--; }else{ break; } } } } public class WindowTest1 { public static void main(String[] args) { // Three thread constructors share the same new Window1(), with three threads in one window Window1 w = new Window1(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("Thread 1"); t2.setName("Thread 2"); t3.setName("Thread 3"); t1.start(); t2.start(); t3.start(); } }
- Comparison of two creation methods:
Compare two ways to create threads:
During development: priority: the way to implement Runnable interface
reason:
1. The implementation method does not have the limitation of single inheritance of classes
2. The implementation method is more suitable to deal with the situation that multiple threads share data. The data shared by multiple threads can be encapsulated in the class implementing the Runnable interface, and then the object of this class can be passed as a parameter to the constructor of threads (Thread class), which is naturally shared data
Contact: public class Thread implements Runnable
The same point: both methods need to override run(), and declare the logic to be executed by the thread in run(). Thread also implements run() in Runnable
8.3. Thread life cycle
8.4. Thread synchronization
- Understand thread safety issues
- Examples of safety problems and Solutions
Example: create three windows to sell tickets, with a total of 100 tickets, by implementing the Runnable interface
1. Problem: in the process of selling tickets, there are duplicate tickets and wrong tickets -- > there is a thread security problem
2. Cause of the problem: when a thread operates the ticket, and the operation has not been completed, other threads also participate and operate the ticket (equivalent to sharing data)
For example, when you go to the toilet, the pit is limited. The pit is equivalent to sharing data. Everyone is a thread. Normally, when you go in, you go out and others come in again. This is a safety problem. Safety problems: thread safety problems occur when you go in and another person comes in
3. How to solve it: when a thread a is operating a ticket, other threads cannot participate in it. Other threads can not start operating a ticket until thread a finishes operating a ticket
This situation cannot be changed even if thread a is blocked
4. In Java, thread safety is solved through synchronization mechanism
Method 1: synchronize code blocks
Synchronized (synchronized monitor){
//Code to be synchronized
}
Note: 1. The code for operating shared data is the code that needs to be synchronized -- > it cannot contain more code or less code. The minimum use principle, otherwise there is no need to increase thread overhead
Less packages: threads may block when executing the remaining synchronization code. There is no lock, and other threads will come in and be confused
Too many packages: if the while package is included, it will execute a complete cycle before releasing the lock. At that time, there will be no tickets, which is equivalent to one thread selling out the tickets
2. Shared data: variables operated by multiple threads. For example, ticket is shared data
3. Synchronization monitor, commonly known as lock. Any class object can act as a lock (this class object will overflow the stack). When entering the toilet, put a lock at this time. Whoever enters will take the lock. Those who do not enter will not get the lock. Whoever can get the lock will operate this code
Synchronization monitor requirements: multiple threads must share the same lock
Add: in the way of implementing Runnable interface to create multithreading, we can consider using this (depending on whether it is the same object) as the synchronization monitor
Mode 2: synchronization method
If the code that operates on shared data is completely declared in a method, this method can be declared synchronous
5. Synchronization mode:
Benefits: solves the thread safety problem
Limitations: when operating synchronous code, only one thread can participate and other threads wait. It is equivalent to a single threaded process (outside or parallel), and the efficiency is low
class Window1 implements Runnable{ // Because only one object of Window1 class is created, the ticket is the same, so static is not used private int ticket = 100; // shared data // Create an object of any class //Object obj = new Object();// To ensure the uniqueness of the lock, you should declare it here. If it is declared in the run method, several threads will create several locks, or the new object in the synchronization monitor will not work Dog dog = new Dog(); // Override run() @Override public void run() { // Methods operated by multiple threads are all in run while (true){ synchronized (dog) { // The same lock must be shared // At this time, the code can be guaranteed to be wrapped. When a process comes in, even if the thread sleep is blocked, other threads have to wait outside until the executing thread wakes up and goes out after the operation. Other threads, including itself (can rob again after robbing), see who goes in again if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":" + ticket); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } ticket--; } else { break; } } } } } public class WindowTest1 { public static void main(String[] args) { // Three thread constructors share the same new Window1(), with three threads in one window Window1 w = new Window1(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("Thread 1"); t2.setName("Thread 2"); t3.setName("Thread 3"); t1.start(); t2.start(); t3.start(); } } // You can also create a lock class class Dog{ }
- Synchronous code blocks deal with thread safety problems in the implementation of Runnable
4. In Java, thread safety is solved through synchronization mechanism
Method 1: synchronize code blocks
Synchronized (synchronized monitor){
//Code to be synchronized
}
Note: 1. The code for operating shared data is the code that needs to be synchronized -- > it cannot contain more code or less code. The minimum use principle, otherwise there is no need to increase thread overhead
Less packages: threads may block when executing the remaining synchronization code. There is no lock, and other threads will come in and be confused
Too many packages: if the while package is included, it will execute a complete cycle before releasing the lock. At that time, there will be no tickets, which is equivalent to one thread selling out the tickets
2. Shared data: variables operated by multiple threads. For example, ticket is shared data
3. Synchronization monitor, commonly known as lock. Any class object can act as a lock (this class object will overflow the stack). When entering the toilet, put a lock at this time. Whoever enters will take the lock. Those who do not enter will not get the lock. Whoever can get the lock will operate this code
Synchronization monitor requirements: multiple threads must share the same lock
Add: in the way of implementing Runnable interface to create multithreading, we can consider using this (depending on whether it is the same object) as the synchronization monitor
class Window1 implements Runnable{ // Because only one object of Window1 class is created, the ticket is the same, so static is not used private int ticket = 100; // It's natural to share data // Create an object of any class //Object obj = new Object(); / / to ensure the uniqueness of the lock, it should be declared here. If it is declared in the run method, several threads will create several locks, or new objects in the synchronization monitor will not work // Dog dog = new Dog(); / / naturally, it is also a shared lock, because only one Window1 object is created // Override run() @Override public void run() { // Methods operated by multiple threads are all in run while (true){ // The loop cannot be wrapped in the synchronous code block. If the while package is included, it will execute a complete loop before releasing the lock. At that time, there will be no tickets, which is equivalent to a thread selling out the tickets // Using this (current object) as a synchronization lock requires only new Window1 objects, so it can only be used for implements, not inheritance // This is the w variable. You can see later that it is a created w object. Here, it is equivalent to dynamic acquisition. The object calling this method is this. This method is defined in Window1, and the object of Window1 is this. Only one Window1 object has been created from beginning to end, so the W of this is unique synchronized (this){ //this: the only object of Window1. Use the current object as / / method 2: synchronized (dog) {/ / the same lock must be shared // At this time, the code can be guaranteed to be wrapped. When a process comes in, even if the thread sleep is blocked, other threads have to wait outside until the executing thread wakes up and goes out after the operation. Other threads, including itself (can rob again after robbing), see who goes in again if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":" + ticket); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } ticket--; } else { break; } } } } } public class WindowTest1 { public static void main(String[] args) { // Three thread constructors share the same new Window1(), with three threads in one window Window1 w = new Window1(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("Thread 1"); t2.setName("Thread 2"); t3.setName("Thread 3"); t1.start(); t2.start(); t3.start(); } } // You can also create a lock class class Dog{ }
- Synchronous code blocks deal with Thread safety issues that inherit the Thread class
Using synchronous code block to solve the Thread safety problem of inheriting Thread class
Example: create three windows to sell tickets by inheriting Thread class
Note: in the way of inheriting Thread class to create multithreads, use this carefully as the synchronization monitor, and consider using the current class (class name. class) as the synchronization monitor to ensure the uniqueness of the object
Reflection is to dynamically obtain the information in the class and call methods with objects after the class is loaded into memory
class Window2 extends Thread{ // Shared data private static int ticket = 100; // private Object obj = new Object(); / / error. At this time, the locks are not unique. Three Window2 objects are newly created. Each object has an instance variable and each thread has an obj. The locks are not shared // private static Object obj = new Object(); / / in this way, three Window2 objects can share one obj // Override the run method in the Thread class @Override public void run() { while (true) { // This is the latter. This is the object of the current Window2 class. Currently, there are three new objects, which are not unique, so this cannot be used here // synchronized (this) {/ / error mode: This represents T1, T2 and T3 objects // synchronized (obj) {/ / correct // Take the current class as the object, and the class is also an object: Class c = Window2.class, class type variable = variable value (equivalent to class type object) // The Window2 class is loaded only once, so the Window2.class object is unique synchronized (Window2.class){ if (ticket > 0) { // Increased probability of wrong ticket try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " Selling tickets,The ticket number is: " + ticket); ticket--; } else { break; } } } } } public class WindowTest2 { public static void main(String[] args) { Window4 w1 = new Window4(); Window4 w2 = new Window4(); Window4 w3 = new Window4(); w1.setName("Window one"); w2.setName("Window II"); w3.setName("Window three"); w1.start(); w2.start(); w3.start(); } }
Synchronous methods deal with thread safety issues
1. The synchronization method still involves the synchronization monitor, but there is no need for explicit declaration, which is equivalent to using the default
2. Non static synchronized method. The synchronization monitor is this
Static synchronized method. The synchronization monitor is the current class itself
- The synchronization method deals with the thread safety problem of implementing Runnable
/** * Using synchronization method to solve the thread safety problem of realizing Runnable interface */ class Window3 implements Runnable{ private int ticket = 100; boolean isFlag = true; // Override the run method of the Runnable interface @Override public void run() { while (isFlag){ show(); } } // Synchronous method: just add the synchronized keyword when declaring the method // The synchronization method can ensure that the code inside the method is wrapped up like the synchronization code block. There are multiple threads outside the method, and there is only one thread in the method, so it is safe // There is a default synchronization lock: this, because this is the only w object public synchronized void show(){ // Synchronization monitor: this (the current object calling the show method) if (ticket > 0){ try { Thread.sleep(10); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Selling tickets,The ticket number is: " + ticket); ticket--; }else{ isFlag = false; } } } public class WindowTest3 { public static void main(String[] args) { Window3 w = new Window3(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("Window 1"); t2.setName("Window 2"); t3.setName("Window 3"); t1.start(); t2.start(); t3.start(); } }
- Synchronous methods handle Thread safety issues that inherit the Thread class
class Window4 extends Thread { // Shared data private static int ticket = 100; // Override the run method in the Thread class @Override public void run() { while (true) { show(); } } // private synchronized void show() {/ / synchronization monitor: three objects: T1, T2 and T3. This method is wrong //Class methods are only loaded once with class loading, and belong to shared methods private static synchronized void show(){ // Synchronization monitor: window4.class (current class). The current class is unique, so it is safe if (ticket > 0) { // Increased probability of wrong ticket try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " Selling tickets,The ticket number is: " + ticket); ticket--; } } } public class WindowTest4 { public static void main(String[] args) { Window4 t1 = new Window4(); Window4 t2 = new Window4(); Window4 t3 = new Window4(); t1.setName("Window one"); t2.setName("Window II"); t3.setName("Window three"); t1.start(); t2.start(); t3.start(); } }
- Lazy style of thread safe singleton mode
The lazy style in singleton mode is rewritten to thread safe with synchronization mechanism
Double check the singleton mode, and remember to add a volatile keyword to the attribute
public class BankTest { public static void main(String[] args) { } } // Lazy style class Bank extends Thread{ private Bank(){ } private static Bank instance = null; // It is possible that the thread enters the blocking state after entering the getInstance method, and then returns to the ready state. When the variable has not been assigned after judging, other threads also come in // Simple processing. Add synchronized directly at the method level. At this time, it is thread safe. When multiple threads call the method, the synchronization lock is bank.class (the current class itself). The class itself also acts as an object, and the lock must be an object // public static synchronized Bank getInstance(){ public static Bank getInstance(){ // Mode 1: slightly poor efficiency //The code inside is an operation on shared data /*synchronized (Bank.class){ if (instance == null){ // Judgment variable instance = new Bank();// Assign values to variables } return instance; }*/ // Method 2: more efficient (interview suggestion writing method 2) // Suppose that the first batch of threads may come together, the getInstance method can come in, and the first if judgment can be passed. They all line up in front of the lock at the same time to see who can grab it, // Suppose that as soon as the thread grabs and the new object goes out, the next few threads have to wait a little longer to enter, because they find that the instance object is not null, they directly take the ready-made instance out, and the next few seem to wait a little longer // However, when the subsequent thread enters the method again, it is judged that the first if and instance are not null. The subsequent thread does not have to wait to enter the synchronization code block again, so it directly takes the created object out, so it is slightly more efficient than mode 1 if (instance == null){ synchronized (Bank.class){ // The two lines of code are operating shared data if (instance == null){ instance = new Bank(); } } } return instance; // It is not regarded as operating shared data } }
- Thread deadlock
Demonstrate thread deadlock
1. Understanding of Deadlock: different threads occupy the synchronization resources required by each other and do not give up,
They are waiting for the other party to give up the synchronization resources they need, forming a thread deadlock
2. Description:
1) After a deadlock occurs, there will be no exception or prompt, but all threads are blocked and cannot continue
2) When using synchronization, avoid deadlocks
public class ThreadTest2 { public static void main(String[] args) { // Create two common class objects: special strings StringBuffer s1 = new StringBuffer(); StringBuffer s2 = new StringBuffer(); // Multithreading is executed at the same time. Who executes first depends on the CPU, so it will fight //Threads can be created anonymously and can only be used once new Thread(){ @Override public void run() { // Nested lock // Hand s1 lock synchronized (s1){ s1.append("a"); s2.append(1); // Deadlock probability increases try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // Hold the lock again synchronized (s2){ s1.append("b"); s2.append(2); System.out.println(s1); System.out.println(s2); } } } }.start(); // Provides an anonymous object of an anonymous implementation class that implements the Runnable interface new Thread(new Runnable() { @Override public void run() { synchronized (s2) { s1.append("c"); s2.append(3); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s1) { s1.append("d"); s2.append(4); System.out.println(s1); System.out.println(s2); } } } }).start(); } }
- Using Lock to solve thread safety problems
Three ways to solve thread safety problems: Lock lock -- > jdk5.0 NEW
Same: both can solve thread safety problems
Difference: the synchronized mechanism automatically releases the synchronization monitor after executing the corresponding synchronization code
Lock requires manual start synchronization (lock()) and manual execution (unlock()) to end synchronization. The operation is more flexible
2. Priority:
Lock - > synchronization code block (it has entered the method body and allocated corresponding resources) - > synchronization method (outside the method body). There is no flexibility to synchronize code blocks
How to solve thread safety problems? There are several ways
- lock solves the thread safety problem of implementing Runnable interface
class Window5 implements Runnable{ // Lock is an interface. Its implementation class ReentrantLock is used // Create a ReentrantLock object in the Runnable implementation class // Fair lock: when the constructor parameter is null, fair is false. When the parameter is true, fair. The queued threads serve first come first // 1. Instantiate ReentrantLock private ReentrantLock lock = new ReentrantLock(); private int ticket = 100; // Override the run method of the Runnable interface @Override public void run() { while (true){ // Try finally is to ensure that unlock must be executed after lock try { // 2. Call the locking method: lock(), which is similar to the thread obtaining the synchronization monitor. The following code is locked from lock(). Ensure that it is single threaded in this process, which is similar to the synchronization code block // When these codes are executed or exceptions occur, the code in finally will also be executed lock.lock(); // Manual locking if (ticket > 0){ try { Thread.sleep(10); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Selling tickets,The ticket number is: " + ticket); ticket--; }else{ break; } }finally { // 3. Call the unlock method: unlock() lock.unlock(); // Manually unlock; if you do not execute this method and lock only, the subsequent code will be single threaded, and may cause hunger and never end } } } } public class LockTest { public static void main(String[] args) { Window5 w = new Window5(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("Window 1"); t2.setName("Window 2"); t3.setName("Window 3"); t1.start(); t2.start(); t3.start(); } }
- lock solves the Thread safety problem of inheriting Thread class
class Window6 extends Thread{ private static int ticket = 100; // Static attributes, multiple windows sell a total of 100 tickets static ReentrantLock lock = new ReentrantLock(); // The lock is static, and multiple threads share the same lock @Override public void run() { while (true){ try { lock.lock(); if (ticket > 0){ try { Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Selling tickets,The ticket number is: " + ticket); ticket--; }else{ break; } }finally{ lock.unlock(); } } } } public class LockTest2 { public static void main(String[] args) { Window6 t1 = new Window6(); Window6 t2 = new Window6(); Window6 t3 = new Window6(); t1.setName("Window 1"); t2.setName("Window 2"); t3.setName("Window 3"); t1.start(); t2.start(); t3.start(); } }
- lock handles thread safety issues in implementing the Runnable interface
class Window6 extends Thread{ private static int ticket = 100; // A total of 100 tickets are sold at multiple windows static ReentrantLock lock = new ReentrantLock(); // The lock is static, and multiple threads share the same lock @Override public void run() { while (true){ try { lock.lock(); if (ticket > 0){ try { Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Selling tickets,The ticket number is: " + ticket); ticket--; }else{ break; } }finally{ lock.unlock(); } } } } public class LockTest2 { public static void main(String[] args) { Window6 t1 = new Window6(); Window6 t2 = new Window6(); Window6 t3 = new Window6(); t1.setName("Window 1"); t2.setName("Window 2"); t3.setName("Window 3"); t1.start(); t2.start(); t3.start(); } }
- Practice of synchronization mechanism
The bank has an account
There are two depositors who deposit 3000 yuan to the same account, 1000 yuan each time, three times. After each deposit, print Zhang Hua's balance
analysis:
1. Is it a multithreading problem? Yes, there are two depositor threads
2. Thread safety depends on whether there is shared data. Shared data: account (balance)
3. Thread safety issues
4. How to solve thread safety? Synchronization mechanism: there are three ways
// Create a class for the account class Account{ private double balance;//balance // Constructor initialization balance public Account(double balance){ this.balance = balance; } // Method of making and saving money // Existing security problems: when a thread performs saving first, it blocks the output in time, and another thread also comes in and saves money. When the first thread wakes up, it will output the balance of the money saved by the two threads // The reason why the synchronization monitor can use this: This is not multiple Customer objects, but the only Account object shared, and two customers share the same Account public synchronized void deposit(double amt){ if (amt > 0){ try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } balance += amt; // The increase in balance is also equivalent to manipulating shared data System.out.println(Thread.currentThread().getName() + "Save money successfully,The balance is: " + balance); } } } // Create a Thread client class that inherits from the Thread class class Customer extends Thread{ // Take the account as the customer's attribute to reflect account sharing private Account acct; // Initializing properties with constructors public Customer(Account acct){ this.acct = acct;//Object as a property } // Override the run method of the Thread class @Override public void run() { // Save money three times for (int i = 0; i < 3; i++) { // Add money to the account acct.deposit(1000); // Object modulation method } } } public class AccountTest { public static void main(String[] args) { // Create an account object Account acct = new Account(0);// Assume the initial value is 0 // At this point, the two customers share the same account Customer c1 = new Customer(acct); Customer c2 = new Customer(acct); // Rename c1.setName("nail"); c2.setName("B"); // Start the Thread. After starting, call the run method that overrides the Thread class c1.start(); c2.start(); } }
8.5. Thread communication
Example of thread communication: print 1 ~ 100 with two threads. Thread 1 and thread 2 print alternately
Three methods involved:
- wait(): once this method is executed, the current thread will enter the blocking state and release the synchronization monitor, which means that other threads can get the synchronization monitor and enter the synchronization code block
- notify(): once this method is executed, one thread that is waiting will be awakened. If multiple threads are waiting, the one with higher priority will be awakened. In short, only one thread can be awakened
- notifyAll(): once this method is executed, all threads that are wait ing will be awakened
explain:
1. The three methods wait(), notify(), notifyall() must be used in the synchronization code block or synchronization method
2. The callers of the three methods wait(), notify(), notifyall() must be the synchronization monitor in the synchronization code block or synchronization method, so they can only be in the above two cases
Otherwise, an IllegalMonitorStateException will occur
The lock method requires Condition to implement thread communication
3. The three methods of wait(), notify(), notifyall() are defined in the java.lang.Ojbect class
The synchronization monitor can be used as any object, and these three methods must be called with this object. It must be ensured that any object must have these methods, so these methods are defined in the Ojbect class
// The call of three methods must be in the synchronization method or synchronization code block, and it is not allowed to include them in lock class Num implements Runnable { private int num = 1;//shared data // Override run method @Override public void run() { while (true) { // synchronized code block synchronized (this) { // This (synchronization monitor) is the object of the current Num class, unique // The thread calling this method can wake up another blocked thread. If multiple threads are wait ing, wake up the one with high priority (probably allocated resources by the CPU) this.notify();// this is omitted if (num <= 100) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + num); num++; // After printing, one thread should be blocked before another thread can come in // After calling the following wait method, release the resource and enter the wait pool, which will release the synchronization lock try { this.wait(); // this is omitted } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } } } } } public class ConnectionTest { public static void main(String[] args) { Num n = new Num(); Thread t1 = new Thread(n); Thread t2 = new Thread(n); t1.setName("Thread 1"); t2.setName("Thread 2"); t1.start(); t2.start(); } }
- Interview question: what are the similarities and differences between sleep() and wait()?
1. The same point: once the method is executed, it can be. The current thread enters the blocking state and needs to handle exceptions
2. Differences:
① The two method declarations are in different locations:
sleep() is declared in the Thread class
wait() is declared in the Ojbect class
② The call requirements are different: sleep() can be called in any required scenario. wait() must be used in the synchronization code block or synchronization method
③ About whether to release the synchronization monitor: if both methods are used in the synchronization code block or synchronization method, sleep() will not release the lock (synchronization monitor), and wait() will release the lock
④ sleep() will wake up automatically, wait() will not wake up automatically, and notify() is required to wake up
- Application of thread communication: Producer / consumer problem
analysis:
1. Is it a multithreading problem? Yes, producer thread, consumer thread
2. Whether there is shared data: Clerk (or product)
3. How to solve the thread safety problem? Synchronization mechanism, there are three methods
4. Thread communication is involved
// Clerk: change clerk to product for better understanding class Clerk{ // It can be understood as clerk bookkeeping // Product attributes private int productCount = 0; // Get products public synchronized void produceProduct(){ if (productCount < 20){ productCount++; System.out.println(Thread.currentThread().getName() + " Start production: " + productCount + "Products"); // Producers can awaken consumers by producing a commodity notify(); }else{ try { wait(); // this can be omitted }catch (InterruptedException e){ e.printStackTrace(); } } } // promoting products public synchronized void comsumeProduct(){ if (productCount > 0){ System.out.println(Thread.currentThread().getName() + ": Consumption section" + productCount + "Products"); productCount--; // As long as a commodity is consumed, the producer can be awakened notify(); }else{ try { wait(); }catch (InterruptedException e){ e.printStackTrace(); } } } } // Producer class class Producer extends Thread{ private Clerk clerk;//Object as shared property // Constructor, shared public Producer(Clerk clerk){ this.clerk = clerk; } // Override run method @Override public void run() { System.out.println(getName() + " : Start production..."); while (true){ try { Thread.sleep(200); }catch (InterruptedException e){ e.printStackTrace(); } // Call production method through shop assistant clerk.produceProduct(); } } } // Consumer category class Comsumer extends Thread{ private Clerk clerk;//Object as shared property // Constructor, shared public Comsumer(Clerk clerk){ this.clerk = clerk; } // Override run method @Override public void run() { System.out.println(getName() + ": Start consuming products..."); while (true){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } // The clerk calls the consumption method clerk.comsumeProduct(); } } } public class ProductTest { public static void main(String[] args) { Clerk clerk = new Clerk(); Producer p1 = new Producer(clerk); Comsumer c1 = new Comsumer(clerk); p1.setName("Producer 1"); c1.setName("Consumer 1"); p1.start(); c1.start(); } }
8.6. Method 3 of creating multithreading: implement Callable interface
The third way to create a thread is to implement the Callable interface. - New in jdk5.0
How to understand that the way to implement the Callable interface to create multithreads is more powerful than the way to implement the Runnable interface to create multithreads?
1.call() can have a return value
2.call() can throw an exception, be caught by external operations, and get the exception information
3.Callable supports generics
// Create an implementation class that implements the Callable interface class NumThread implements Callable{ // Implement (rewrite) the call method of the Callable interface, declare the operation to be performed by this thread in call(), and the method can have a return value. If not, return null @Override public Object call() throws Exception { int sum = 0; for (int i = 0; i <= 100; i++) { if (i % 2 == 0){ System.out.println(i); sum += i; } } return sum;// It is equivalent to converting int to Integer: automatic boxing, and then assigning Integer as a subclass to Object, reflecting polymorphism } } public class ThreadNew { public static void main(String[] args) { // 3. Create an object of the Callable interface implementation class NumThread numThread = new NumThread(); // The Callable interface is implemented, but there is no Thread class as the parent class, so the start method cannot be called directly // To use threads, you need to borrow the FutureTask class // 4. Pass the object of this Callable interface implementation class as a parameter to the FutureTask constructor to create the FutureTask object FutureTask futureTask = new FutureTask(numThread);// There is a constructor to pass an object that implements the implementation class of the Callable interface // The starting thread must the newThread object and call the start method // The interface implemented by futureTask inherits the Runnable interface, which is equivalent to implementing the Runnable interface, so polymorphism is reflected here: Runnable target = new futureTask(); // After start, the thread will enter the ready state // 5. Pass the FutureTask object as a parameter to the constructor of the Thread class, create the Thread object, and call start() new Thread(futureTask).start(); // The Thread constructor argument can be put into futureTask without error, because futureTask implements both Runnable and Future interfaces // Call the get method of this object to return a value try { // 6. Get the return value of call method in Callable // The return value of get() is the return value of call() overridden by the FutureTask constructor parameter Callable implementation class Object sum = futureTask.get();//The return value of the call method is received with a variable whose value is the return value of the call method System.out.println("The sum is: " + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
- New method 2: use thread pool
Four ways to create threads: using thread pool
Benefits:
1. Improve response speed (reduce the time to create new threads)
2. Reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)
3. Facilitate thread management: you can set the properties of thread pool to limit the creation of thread pool, including the maintenance of thread pool
corePoolSize: the size of the core pool
maximumPoolSize: maximum number of threads
keepAliveTime: when the thread has no task, how long will it last at most and then terminate
Interview question: there are four ways to create multithreading:
// Create a thread class class NumberThread implements Runnable{ // Rewrite the run method to traverse even numbers from 1 to 100 @Override public void run() { for (int i = 0; i <= 100; i++) { if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } } // Other thread classes in the same thread pool class NumberThread1 implements Runnable{ @Override public void run() { for (int i = 0; i <= 100; i++) { if (i % 2 != 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } } public class ThreadPool { public static void main(String[] args) { // 1. Provide a thread pool with a specified number of threads // Executors: tool class, factory class of thread pool, which is used to create and return different types of thread pools // Call the static method in the tool class to reuse the thread pool with a fixed number of threads // The return value is the object of the implementation (sub) class (ThreadPoolExecutor) of the thread pool ExecutorService interface type, which reflects polymorphism. This is not a new object ExecutorService service = Executors.newFixedThreadPool(10); // Create a thread pool with 10 threads // Set the properties of the thread pool System.out.println(service.getClass());// Gets which class the object is made of // Service is the parent class and service1 is the child class. If the parent class wants to use the unique methods in the child class, it must be forced downward ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; service1.setCorePoolSize(15);// The properties in the class can be variables, so they can be set. The properties in the interface can only be constants and cannot be set // service1.setKeepAliveTime(); // Call the execution method and pass in the object of the implementation class of the Runnable interface to be executed. Naturally, you know what the thread's run method needs to do // The purpose of providing method parameters is to know what the thread is going to do // 2. To execute the operation of the specified thread, you need to provide an object that implements the Runnable interface or Callable interface implementation class service.execute(new NumberThread());// No return value, applicable to Runnable // Create a new thread service.execute(new NumberThread1()); // service.submit(Callable callable);// There is a return value, which is applicable to callable. The submit method returns a value of callable type, which can be received with an object of FutureTask type, and then call get() to view the return value // 3. The connection pool can be closed without thread pool service.shutdown(); } }