JUC high concurrency programming
1. Introduction to JUC
-
In Java, the thread part is a key point. JUC is the inspection of the Java.util.concurrent toolkit, which is a toolkit for processing threads. JDK1.5 began to appear
-
Processes and threads:
- Process: an application running in the system; Once a program runs, it is a process, which is the smallest unit of resource allocation
- Thread: the basic unit in which the system allocates processor time resources, or an execution flow executed independently within a process. The smallest unit of program execution.
-
Status of thread:
- New new
- Runnable ready
- Blocked
- Waiting
- TimedWaiting (obsolete)
- Terminated
-
Difference between Wait and Sleep
- sleep is the static method of Thread and wait is the method of Object. Any Object can be called by real columns
- sleep does not release the lock, nor does it need to occupy the lock. wait releases the lock, but the premise for calling it is that the current thread occupies the lock (that is, the code should be in synchronized)
- They can all be interrupted by the interrupted method
-
Concurrency and parallelism
-
Let's start with: serial and parallel
- Serial mode: only one task can be acquired and executed at a time.
- Parallel mode: obtain multiple tasks at the same time and execute the obtained tasks at the same time.
-
Concurrency: it refers to the imagination that multiple programs can run at the same time. More specifically, multiple threads can run at the same time or multiple instructions can run at the same time
-
Concurrency: multiple threads are accessing the same resource at the same time, and multiple threads are connected to one point
For example: Spring Festival transportation ticket grabbing, e-commerce second kill
-
Parallel: multiple tasks are executed together and then summarized
For example: soak instant noodles, boil water in an electric kettle, tear the seasoning bag and pour it into the bucket
-
-
Tube side
- Monitor (called monitor in operating system and lock in Java): it is a synchronization mechanism to ensure that only one thread accesses protected data or code at the same time
- The same in the Jvm is based on entry and exit and is implemented using a management object
-
User thread and daemon thread
-
User threads: Custom threads. Most of them are user threads
Case: the main thread ends: the user thread is still running and the jvm is alive
package com.codetip.codejuc.juc; public class Test01 { public static void main(String[] args) { // isDaemon indicates whether it is a user thread or a daemon thread // true: daemon thread false user thread new Thread(() -> { System.out.println(Thread.currentThread().getName() + "::" + Thread.currentThread().isDaemon()); // Set the loop not to terminate while (true) { } }, "aa").start(); System.out.println(Thread.currentThread().getName() + " Over"); } }
-
Daemon thread: a specific thread in the background, such as garbage collection
Case: without user threads, the jvm will end if all threads are daemon threads
package com.codetip.codejuc.juc; public class Test01 { public static void main(String[] args) { // isDaemon indicates whether it is a user thread or a daemon thread // true: daemon thread false user thread Thread aa = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "::" + Thread.currentThread().isDaemon()); while (true) { } }, "aa"); // Set thread to daemon aa.setDaemon(true); aa.start(); System.out.println(Thread.currentThread().getName() + " Over"); } } // The execution results are shown in the figure below
-
2. Lock interface
-
Review Synchronized: Synchronized is a keyword in Java. It is a kind of synchronization lock. There are the following types of modified objects (scope of action)
-
synchronized scope
// Decorated code block synchronized(this){ // Modify a code block. The modified code block is a synchronization method. Its scope is the code in braces {}, and the object is the object calling the code block. } // Modify a method public synchronized add(){ // Modify a code block. The modified code block is a synchronous method. Its scope is the whole method, and the object is the object calling the method } // Modify a static method public static synchronized save(){ // Modifies a static method, the scope of action is the whole static method, and the action object is all objects of this class } // Decorate a class public synchronized class test001(){ // The scope is the content enclosed in braces after synchronized, and the scope object is all objects of this class }
-
Multithreaded programming steps
- Create a resource class, and create the methods and properties of the resource class
- Operation methods in resource class: 1. Judge 2. Work 3. Notify
- Create multiple threads and call the operation methods of the resource class
-
Several ways to create threads
- Integrate Thread class (generally not used. In java, it is single inheritance, and inheritance is very precious)
- Implement Runnable interface
- Use the Callable interface (explained later)
- Using thread pool (explained later)
-
Example of synchronizing ticket purchase
-
Three conductors, selling 30 tickets
package com.codetip.codejuc.juc.sync; // The first step is to create a resource class and define properties and operation methods class Ticket { // Number of votes private int number = 30; // Ticketing method public synchronized void sale() { // Ticket buying process if (number > 0) { System.out.println(Thread.currentThread().getName() + ":Sell a ticket: " + 1 + " surplus:" + --number); } } } public class SaleTicked { public static void main(String[] args) { Ticket ticket = new Ticket(); // Thread one new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 40; i++) { ticket.sale(); } } }, "aa").start(); // Thread two new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 40; i++) { ticket.sale(); } } }, "bb").start(); // Line Cheng San new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 40; i++) { ticket.sale(); } } }, "cc").start(); } }
-
-
-
Lock interface
- Description: provides a framework interface and class for lock and wait conditions. It is different from the built-in synchronization and monitor. Details: check the API document (Baidu)
- It is not always necessary to call the start method in the thread to create the thread immediately. There is a native keyword, which calls the command of the operating system. If the system is idle at that time, a thread may be created immediately.
Rewrite the ticketing code with lock:
package com.codetip.codejuc.juc.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; // The first step is to create a resource class and define properties and operation methods class LockTicket { private final Lock lock = new ReentrantLock(); // Number of votes private int number = 30; // Ticketing method public void sale() { try { // Lock lock.lock(); // Ticketing process if (number > 0) { System.out.println(Thread.currentThread().getName() + ":Sell a ticket: " + 1 + " surplus:" + --number); } } finally { // Unlock lock.unlock(); } } } public class LockTicked { public static void main(String[] args) { LockTicket ticket = new LockTicket(); // Thread one new Thread(() -> { for (int i = 0; i < 40; i++) { ticket.sale(); } }, "aa").start(); // Thread two new Thread(() -> { for (int i = 0; i < 40; i++) { ticket.sale(); } }, "bb").start(); // Line Cheng San new Thread(() -> { for (int i = 0; i < 40; i++) { ticket.sale(); } }, "cc").start(); } }
3. Inter thread communication
-
Case: there are two threads to implement a variable with an initial value of 0. A thread pair value plus 1 and a thread pair value minus 1
// Using the synchronized keyword package com.codetip.codejuc.juc.number; // The first step is to create a resource class and define properties and methods class Share { // attribute private int num = 0; // Method plus 1 public synchronized void incr() throws InterruptedException { // Judge, work, inform if (num != 0) { // Judge whether num is equal to 0. If not, wait this.wait(); } // If num is 0, add 1 num++; System.out.println(Thread.currentThread().getName() + "The value is:" + num); // Notify other threads this.notifyAll(); } // Method minus 1 public synchronized void decr() throws InterruptedException { // Judge, work, inform if (num != 1) { // Judge whether num is equal to 1. If not, wait this.wait(); } // If num is 0, subtract 1 num--; System.out.println(Thread.currentThread().getName() + "The value is:" + num); // Notify other threads this.notifyAll(); } } public class Number { public static void main(String[] args) { Share share = new Share(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "a").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "b").start(); } }
The results are as follows:
-
False wake-up problem: add two threads c and d in the code
package com.codetip.codejuc.juc.number; // The first step is to create a resource class and define properties and methods class Share { // attribute private int num = 0; // Method plus 1 public synchronized void incr() throws InterruptedException { // Judge, work, inform if (num != 0) { // Judge whether num is equal to 0. If not, wait this.wait(); } // If num is 0, add 1 num++; System.out.println(Thread.currentThread().getName() + "The value is:" + num); // Notify other threads this.notifyAll(); } // Method minus 1 public synchronized void decr() throws InterruptedException { // Judge, work, inform if (num != 1) { // Judge whether num is equal to 1. If not, wait this.wait(); } // If num is 0, subtract 1 num--; System.out.println(Thread.currentThread().getName() + "The value is:" + num); // Notify other threads this.notifyAll(); } } public class Number { public static void main(String[] args) { Share share = new Share(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "a").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "b").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "c").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "d").start(); } }
Execution results:
See a different effect than expected
The reason for this is: wait here. Specific instructions in wait in API: for the version that touches a parameter, it is possible to realize interrupt and false wake-up, and this method should always be used in the loop
In fact, the reason is: A will wake up other locks after normal execution, but after wake-up, the thread cannot determine which is executing, and the characteristics of the wait method are where to sleep and where to wake up, so some threads do not execute the judgment method again.
The modified code is as follows: (modify if to a while loop)
package com.codetip.codejuc.juc.number; // The first step is to create a resource class and define properties and methods class Share { // attribute private int num = 0; // Method plus 1 public synchronized void incr() throws InterruptedException { // Judge, work, inform while (num != 0) { // Judge whether num is equal to 0. If not, wait this.wait(); } // If num is 0, add 1 num++; System.out.println(Thread.currentThread().getName() + "The value is:" + num); // Notify other threads this.notifyAll(); } // Method minus 1 public synchronized void decr() throws InterruptedException { // Judge, work, inform while (num != 1) { this.wait(); } // If num is 1, subtract 1 num--; System.out.println(Thread.currentThread().getName() + "The value is:" + num); // Notify other threads this.notifyAll(); } } public class Number { public static void main(String[] args) { Share share = new Share(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "a").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "b").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "c").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "d").start(); } }
The results are as follows:
-
Implementation through lock interface
Create Lock class
Create a monitoring class for lock,
Lock lock = new ReentrantLock(); // Monitor condition of lock = lock. Newcondition();
package com.codetip.codejuc.juc.number;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;// Step 1: create a resource class and define the attribute and method class ShareL {/ / attribute private int num = 0; lock lock = new reentrantlock(); / / monitor condition of lock = lock. Newcondition(); / / method plus 1 public void incr() throws interruptedexception {/ / judge, work and notify lock. Lock(); try {while (Num! = 0) {/ / judge whether num is equal to 0. If not, wait for condition.await();} / / if num is 0, add 1 to operate num + +; system.out.println (thread. Currentthread(). Getname() + "value is:" + num); / / notify other threads of condition. Signalall() ;} finally {lock. Unlock();}} / / method minus 1 public void decr() throws interruptedexception {lock. Lock(); try {/ / judge, work and notify while (Num! = 1) {condition.await();} //If num is 1, subtract 1. Operate num --; system. Out. Println (thread. Currentthread(). Getname() + "value is:" + num); / / notify other threads of condition. Signalall();} finally {lock. Unlock();}}} public class locknumber {public static void main (string [] args) { ShareL share = new ShareL(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "a").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "b").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.incr() ; } catch (InterruptedException e) { e.printStackTrace(); } } }, "c").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { try { share.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "d").start(); }}
4. Customized communication between threads
Causes threads to execute in the specified order
Case: start three threads, AA print five times, BB print 10 times, CC print 15 times, and conduct 10 rounds of test
Scheme: design a flag bit flag ==1 for printing, modify the flag bit, notify BB, and so on AA flag==2 BB flag ==3 CC
package com.codetip.codejuc.juc.custom;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class ShareResource { // Custom flag bit private int flag = 1; // 1:AA 2:BB 3:CC / / create Lock lock = new ReentrantLock(); / / create three conditions (equivalent to listening); Condition C1 = lock. Newcondition(); Condition C2 = lock. Newcondition(); Condition C3 = lock. Newcondition(); / / print public void print5 (int loop) five times Throws interruptedexception {/ / step 1 lock. Lock(); try {/ / put it into the loop to prevent false wake-up while (flag! = 1) {C1. Await();} for (int i = 1; I < = 5; I + +) {system.out.println (thread. Currentthread(). Getname() + "loop value:" + I "+ "The current is the" + loop + "round");} / / modify the flag bit to notify BB flag = 2; C2. Signal();} finally {lock. Unlock();}} / / print public void print10(int loop) throws InterruptedException {/ / step 1 lock. Lock(); try {/ / put it into the loop to prevent false wake-up while (flag! = 2) {C2. Await();} for (int i = 1; I < = 10; I + +) {system.out.println (thread. Currentthread(). Getname() + "value of loop:" + I + "current" + loop + "round");} //Modify flag bit notification CC flag = 3; C3. Signal();} finally {lock. Unlock();}} / / print public void print15(int loop) throws InterruptedException {/ / step 1 lock. Lock(); try {/ / put it into the loop to prevent false wake-up while (flag! = 3) {C3. Await();} for (int i = 1; I < = 15; I + +) {system.out.println (thread. Currentthread(). Getname() + "value of loop:" + I + "current" + loop + "round");} //Modify flag bit notification AA flag = 1; C1. Signal();} finally {lock. Unlock();}}} public class threadprint {public static void main (string [] args) {shareresource shareresource = new shareresource(); new thread (() - > {for (int i = 0; I < = 10; I + +) { try { shareResource.print5(i); } catch (InterruptedException e) { e.printStackTrace(); } } }, "AA").start(); new Thread(() -> { for (int i = 0; i <= 10; i++) { try { shareResource.print10(i) ; } catch (InterruptedException e) { e.printStackTrace(); } } }, "BB").start(); new Thread(() -> { for (int i = 0; i <= 10; i++) { try { shareResource.print15(i); } catch (InterruptedException e) { e.printStackTrace(); } } }, "CC").start(); }}
The operation is as follows: