JUC (concurrent programming)
1. What is JUC
java.util.concurrent package is a tool class used in concurrent programming. It has the following three packages:
java.util Toolkit
Business: common Thread code Thread
Runnable has no return value and its efficiency is relatively lower than that of Callable!)
(
2. Threads and processes
Threads and processes
Process: a program, QQ exe,music.exe, a collection of programs
A process often contains multiple threads, including at least one
Java has two threads by default? 2 main threads, GC threads
Thread: open a process Typora, write, and save automatically (thread is responsible for)
For java: Thread, Runnable, Callable
Can Java really start threads? I can't drive!
public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0(); // Local methods, call the underlying c + +, java can not directly operate the hardware!
Concurrency and parallelism
Concurrent programming: concurrency and parallelism
Concurrency (multiple threads operate on the same resource)
- With one CPU core, multiple threads can be simulated. The world's martial arts can only be broken quickly! Fast alternation
Parallel (multiple people walking together)
- The CPU is multi-core, and multiple threads execute together; Use thread pool)
Code view cpu cores
package com.kuang.juc.demo01; public class Test { public static void main(String[] args) { // Gets the number of cores of the CPU // CPU intensive, IO intensive System.out.println(Runtime.getRuntime().availableProcessors()); } }
The essence of concurrent programming: making full use of CPU resources
Several states of threads
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, // newborn /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, // function /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, // block /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, // Wait (dead, etc.), block /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, // Timeout waiting /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; // termination }
wait/sleep difference
1. From different classes
wait => Object
sleep => Thread
2. About lock release
wait will release the lock. Sleep will sleep with the lock. It won't release!
3. The scope of use is different
wait must be used in synchronous code blocks
Sleep can sleep anywhere and can be used anywhere
4. Need to catch exceptions
wait also needs to catch exceptions
sleep must catch exceptions
3. Lock lock
Traditional synchonized
package com.kuang.juc.demo01; // Basic ticket example /** * Real multi-threaded development, reduce coupling! * Thread is a separate resource class without any affiliated operations! * 1.Properties and methods */ public class SaleTicketDemo01 { public static void main(String[] args) { // Concurrency: multiple threads operate on the same resource class. Just throw the resource class into the thread! Ticket ticket = new Ticket(); new Thread(()->{ for (int i = 1; i < 40; i++) { ticket.sale(); } },"A").start(); new Thread(()->{ for (int i = 1; i < 40; i++) { ticket.sale(); } },"B").start(); new Thread(()->{ for (int i = 1; i < 40; i++) { ticket.sale(); } },"C").start(); } } // Resource class OOP class Ticket{ // Properties, methods private int nums = 30; // How to sell tickets // synchronized essence: queue + lock public synchronized void sale(){ if (nums>0){ System.out.println(Thread.currentThread().getName()+"Sold"+nums--+"Tickets. Remaining: "+nums+"Ticket"); } } }
result:
)
summary
After adding the synchonized keyword, it becomes a synchronization method (queue locking). It's equivalent to you going to the school canteen to buy rice. The window to buy rice is lined up one by one. After the people in the row went to give the bowl to their aunt, it's equivalent to adding a lock to themselves. After the aunt finished the meal, she gave the bowl to you, which is equivalent to releasing the lock. After the release, the people behind can line up to buy food again.
Lock interface
)
)
)
Fair lock: very fair. There is a first come first served order 3h 3s
Unfair lock: very unfair. You can jump the queue (default)
lock to buy tickets
package com.kuang.juc.demo01; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SaleTicketDemo02 { public static void main(String[] args) { // Concurrency: multiple threads operate on the same resource class. Just throw the resource class into the thread! Ticket2 ticket = new Ticket2(); new Thread(()->{ for (int i = 1; i < 40; i++) { ticket.sale(); } },"A").start(); new Thread(()->{ for (int i = 1; i < 40; i++) { ticket.sale(); } },"B").start(); new Thread(()->{ for (int i = 1; i < 40; i++) { ticket.sale(); } },"C").start(); } } // lock class Ticket2{ // Properties, methods private int nums = 30; Lock lock = new ReentrantLock(); public void sale(){ lock.lock(); //Lock try { // Business code if (nums>0){ System.out.println(Thread.currentThread().getName()+"Sold"+nums--+"Tickets. Remaining: "+nums+"Ticket"); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); //Unlock } } }
The difference between synchronized and Lock
- Synchoizned is a built-in keyword; Lock is a java class
- Synchonized cannot judge the state of obtaining the lock; Lock can determine whether a lock has been obtained
- Synchronized will automatically release the lock; Lock, you must release the lock manually! If the lock * * is not released, it will deadlock**
- Synchronized thread 1 (lock acquisition, blocking), thread 2 (wait, silly, etc.); Lock doesn't have to wait!
- Synchronized reentrant lock, non interruptible, unfair; Lock, reentrant lock, lock judgment, unfair (you can set it yourself)
- Synchronized is suitable for locking a small number of code synchronization problems; Lock is suitable for locking a large number of synchronization codes!
What is a lock? How to judge who is locked?
4. Producer consumer issues
Interview: Singleton mode, sorting algorithm, producer consumer problem, deadlock
Synchonized version of producer consumer issues
package com.kuang.juc.pc; /** * Communication problems between threads: producer and consumer problems! Wait for wake-up, notify wake-up * Threads alternately perform a and B operations, and the same variable num = 0 * A num+1 * B num-1 */ public class A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.increment(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.decrement(); } },"B").start(); } } // Pithy formula: judge, wait, business, notice! class Data{ // Digital resources private int num = 0; //+1 public synchronized void increment(){ if (num != 0){ // 0 // wait for try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } num++; System.out.println(Thread.currentThread().getName()+"==>"+num); // Notify other threads that I + 1 is over this.notifyAll(); } //-1 public synchronized void decrement(){ if (num == 0){ //1 // wait for try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } num--; System.out.println(Thread.currentThread().getName()+"==>"+num); // Notify other threads that I -1'm done this.notifyAll(); } }
The problem exists. There are 4 threads a, B, C and D
)
There was a problem and 2 and 3 were found
solve
)
Judge the original if = = > while to prevent false wake-up!
Note: if can only be judged once (if is not judged after the second thread enters, it is directly + 1, and 2 occurs, which is unsafe!), When using while, you will judge every time you enter this method, which is very safe!
Producer consumer issues of JUC version
)
Find the Condition through lock
)
code implementation
package com.kuang.juc.pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Communication problems between threads: producer and consumer problems! Wait for wake-up, notify wake-up * Threads alternately perform a and B operations, and the same variable num = 0 * A num+1 * B num-1 */ public class B { public static void main(String[] args) { Data2 data = new Data2(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); } } class Data2{ // Digital resource class private int num = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //condition.await(); // wait for //condition.signalAll(); // awaken //+1 public void increment() throws InterruptedException { lock.lock(); //Lock try { // Business code while (num != 0){ // 0 // wait for condition.await(); } num++; System.out.println(Thread.currentThread().getName()+"==>"+num); // Notify other threads that I + 1 is over! condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); //Unlock } } //-1 public void decrement() throws InterruptedException { lock.lock(); // Lock try { // Business code while (num == 0){ //1 condition.await(); } num--; System.out.println(Thread.currentThread().getName()+"==>"+num); // Notify other threads that I -1'm done! condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); //Unlock } } }
result:
)
Question: if you want A to finish B, C and D
Any new technology is definitely not only covering the original technology, but also has its own advantages and supplements!
Condition precise notification and wake-up thread
Code implementation:
package com.kuang.juc.pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * A B C 3 Sequential execution of threads * A-->B-->C-->A...... */ public class C { public static void main(String[] args) { Data3 data = new Data3(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.printA(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.printB(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.printC(); } },"C").start(); } } class Data3 { // Resource class private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); //Synchronization monitor private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); private int flag = 1; // 1A 2B 3C public void printA() { lock.lock(); try { // Business code, judgment, waiting, execution, notification, Trilogy while (flag != 1) { //wait for condition1.await(); } System.out.println(Thread.currentThread().getName() + "=>AAAAAAAAAA"); // Wake up, wake up the designated person, B flag = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB() { lock.lock(); try { while (flag != 2) { condition2.await(); } System.out.println(Thread.currentThread().getName() + "=>BBBBBBBBBB"); // Wake up, wake up the designated person, C flag = 3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC() { lock.lock(); try { while (flag != 3) { condition3.await(); } System.out.println(Thread.currentThread().getName() + "=>CCCCCCCCCC"); // Wake up, wake up the designated person, A flag = 1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
View results:
5. 8 lock phenomenon
How to judge who the lock is? Always know what a lock is and who it is?
Object, class
Deep understanding of our locks
5.1 test I
package com.kuang.juc.lock8; import java.util.concurrent.TimeUnit; /** * 8 Lock is about 8 questions about lock * 1,Under standard conditions, do two threads print, call or send text messages first? 1 / send text messages 2 / make phone calls * 2,Sending SMS is delayed for 4s. Do the two threads print, call or send SMS first? 1 / send text messages 2 / make phone calls Cause: only one lock of the caller was obtained! */ public class Test1 { public static void main(String[] args) { Phone phone = new Phone(); // Existence of lock new Thread(()->{ phone.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); },"B").start(); } } class Phone{ // The object of the synchronized lock is the method caller! // The two methods use the same lock. Whoever gets it first will execute it! public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("send message"); } public synchronized void call(){ System.out.println("phone"); } }
5.2 test II
package com.kuang.juc.lock8; import java.util.concurrent.TimeUnit; /** * 3,After adding a common method, print and send text messages first or hello? 1 / Hello 2 / common method of sending SMS * 4,Two objects, two synchronization methods, print and send text messages or print and call first? 1 / call 2 / text * */ public class Test2 { public static void main(String[] args) { // Two objects, two callers, two locks! Phone2 phone1 = new Phone2(); Phone2 phone2 = new Phone2(); // Existence of lock new Thread(()->{ phone1.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone2.call(); },"B").start(); } } class Phone2{ // The object of the synchronized lock is the method caller! public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("send message"); } public synchronized void call(){ System.out.println("phone"); } // There's no lock here! It is not a synchronization method and is not affected by the lock! public void hello(){ System.out.println("hello"); } }
5.3 test III
package com.kuang.juc.lock8; import java.util.concurrent.TimeUnit; /** * 5,Add two static synchronization methods. There is only one object. Print and send text messages or call first? 1 / send text messages 2 / call because the lock is class, and there is only one class template, one lock! * 6,Two objects, two static synchronization methods, print, send text messages or call first? 1 / send text messages 2 / call because the lock is class, and there is only one class template, one lock! * */ public class Test3 { public static void main(String[] args) { // There is only one class template for the two objects, static, and the lock is class Phone3 phone1 = new Phone3(); Phone3 phone2 = new Phone3(); // Existence of lock new Thread(()->{ phone1.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone2.call(); },"B").start(); } } // Phone3 has only one class object class Phone3{ // The object of the synchronized lock is the method caller! // Static static methods are available as soon as the Class is loaded! The lock is Class // Lock Class template! public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("send message"); } public static synchronized void call(){ System.out.println("phone"); } // There's no lock here! It is not a synchronization method and is not affected by the lock! public void hello(){ System.out.println("hello"); } }
5.4 test IV
package com.kuang.juc.lock8; import java.util.concurrent.TimeUnit; /** * 1,1 A static synchronization method, a common synchronization method, an object, print, send text messages or call first? 1 / call 2 / text * Cause of 1: call to call a lock, and the lock is the caller; Call another lock for texting. The class template of the lock is class! * 2,1 Two static synchronization methods, one common synchronization method and two objects. Print and send text messages or call first? 1 / call 2 / text * Cause of 2: call to call a lock, and the lock is the caller phone2; Call another lock for texting. The class template of the lock is class! */ public class Test4 { public static void main(String[] args) { // The lock obtained is the class template class Phone4 phone1 = new Phone4(); // The lock obtained is caller phone2 Phone4 phone2 = new Phone4(); // Existence of lock new Thread(()->{ phone1.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone2.call(); },"B").start(); } } class Phone4{ // The static synchronization method locks the class class template public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("send message"); } // Common synchronization methods lock the caller public synchronized void call(){ System.out.println("phone"); } }
summary
In the above example, it is found that:
- New = > the lock is a specific mobile phone of this
- Static = > the lock is the only template of class
synchronized is the basis of synchronization: every object in java can be used as a lock!
The specific manifestations are as follows:
- For normal synchronization methods, the lock is the current instance object (this)
- For static synchronization methods, the lock is the current Class object
- For synchronous method blocks, locks are configuration objects in synchronized parentheses
6. Collection class unsafe
List is not safe
package com.kuang.juc.unsafe; import java.lang.reflect.Array; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; // java. util. Concurrent modificationexception concurrent modification exception! public class ListTest { public static void main(String[] args) { // Is it unsafe to send an ArrayList? synchonized; /** * Solution: * 1, List<String> list = new Vector<>(); * 2, List<String> list = Collections.synchronizedList(new ArrayList<>()); * 3, List<String> list = new CopyOnWriteArrayList<>(); */ // CopyOnWrite is an optimization strategy in the field of copy COW computer programming when writing! // When multiple threads call, list, read, fixed, write (there may be overwrite) // Avoid data problems caused by overwriting when writing! // CopyOnWriteArrayList is better than Vector. Where is it? // Vector (synchonized is used in the add method, which is inefficient) // CopyOnWriteArrayList (the add method uses lock lock, which is more efficient than vector) List<String> list = new CopyOnWriteArrayList<>(); for (int i = 1; i <= 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },String.valueOf(i)).start(); } } }
To view the add method of CopyOnWriteArrayList:
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-a5aauini-1625379008053)()]
Learning methods: 1. Be able to use it first. 2. Compare goods and find other solutions. 3. Analyze the source code
Set is not safe
package com.kuang.juc.unsafe; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; // java. util. Concurrent modificationexception concurrent modification exception public class TestSet { public static void main(String[] args) { /** * Solution: * 1,Set<String> set = Collections.synchronizedSet(new HashSet<>()); Convert to synchonized by tool class * 2,Set<String> set = new CopyOnWriteArraySet<>(); Copy on write for performance and security! */ Set<String> set = new CopyOnWriteArraySet<>(); for (int i = 1; i <= 30; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(set); }).start(); } } }
What is the bottom layer of HashSet?
public HashSet() { map = new HashMap<>(); } // add. The essence of ashset is the key of map, because the key cannot be repeated! public boolean add(E e) { return map.put(e, PRESENT)==null; } // PRESENT. Constant value! private static final Object PRESENT = new Object();
Map is not secure
Review the basic operation of map
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-cepap1zo-1625379008055)()]
package com.kuang.juc.unsafe; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; // java. util. Concurrent modificationexception concurrent modification exception! public class MapTest { public static void main(String[] args) { // Is map used like this? No, HashMap is not used in multithreading // What is the default equivalent? // Load factor, initialization capacity /** * resolvent: * 1, Map<String, String> map = Collections.synchronizedMap(new HashMap<>()); The tool class is replaced by a safe map collection * 2, Map<String, String> map = new ConcurrentHashMap<>(); **/ Map<String, String> map = new ConcurrentHashMap<>(); for (int i = 1; i <= 30 ; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5)); System.out.println(map); },String.valueOf(i)).start(); } } }
7. Walk into Callable
1. Can have return value
2. Exceptions can be thrown
3. The methods are different, run() / call()
package com.kuang.juc.callable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { // new Thread(new Runnable()).start(); // new Thread(new FutureTask<V>()).start(); // new Thread(new FutureTask<V>( Callable() )).start(); new Thread().start(); //How do I start Callable MyThread myThread = new MyThread(); // Callable interface is implemented FutureTask futureTask = new FutureTask<>(myThread); // Adaptation class new Thread(futureTask,"A").start(); new Thread(futureTask,"B").start(); // With cache Integer o = (Integer) futureTask.get(); // This get method may cause blocking! Put him last System.out.println(o); } } class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { // If there are time-consuming operations System.out.println("call()"); return 1024; } }
result:
Details:
1. With cache
2. The result may need to wait and block!
8. Common auxiliary classes (must focus)
8.1. Countdownlatch
Case: in the evening, the master closes the door of the study room
package com.kuang.juc.add; import java.util.concurrent.CountDownLatch; // Counter public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { // The total is six people in six classrooms // Use it when you have to perform a task CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+"go out"); countDownLatch.countDown(); // Quantity - 1 },String.valueOf(i)).start(); } countDownLatch.await(); //Wait for the counter to return to zero, and then execute down! System.out.println("I'm your uncle. I'm closing the door!"); } }
result:
Principle:
countDownLatch.countDown() / / quantity - 1
countDownLatch.await() / / wait until the counter is reset to zero, and then execute downward!
Every time * * countDown() * * is called by a thread, the number is - 1, assuming that the counter becomes 0** countDownLatch.await() * * will be awakened and continue to execute downward!
8.2. Cyclicbarrier (addition counter)
Equivalent to addition counter
Case: gather 7 dragon balls to summon the divine dragon
package com.kuang.juc.add; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * eg: Gather 7 dragon balls to summon the dragon! */ public class CyclicBarrierDemo { public static void main(String[] args) { // Thread calling Dragon Ball CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("Summon dragon successfully!"); }); for (int i = 0; i < 7; i++) { // Can lamabda operate on variable i? no final int temp = i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"Collected"+temp+"Dragon Ball"); try { cyclicBarrier.await(); //wait for } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } } }
8.3 semaphore
Case: grab a parking space
6 cars - 3 parking positions
package com.kuang.juc.add; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * Grab a parking space */ public class SemaphoreDemo { public static void main(String[] args) { // Number of threads, parking space current limit!! Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 6; i++) { new Thread(()->{ // acquire() is released by release() try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"Grab a parking space"); TimeUnit.SECONDS.sleep(2); // Simulated parking for 2s System.out.println(Thread.currentThread().getName()+"Leave the parking space"); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release(); } },String.valueOf(i)+"Car No").start(); } } }
result:
Principle:
semaphore.acquire(), assuming it is full, wait until it is released
semaphore.release() releases the current semaphore by + 1, and then wakes up the waiting thread!
Usage scenario:
1. Use when multiple shared resources are mutually exclusive!
2. Concurrent flow restriction, control the maximum number of threads!
9. Read write lock
9.1,ReadWriteLock
Reads can be read simultaneously by multiple threads
When writing, only one thread can write
package com.kuang.juc.rw; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * ReadWriteLock * Read -- read can coexist! * Reading and writing cannot coexist! * Write -- write cannot coexist! */ public class ReadWriteLockDemo { public static void main(String[] args) { //MyCache myCache = new MyCache(); MyCacheLock myCache = new MyCacheLock(); // write in for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(()->{ myCache.put(temp+"",temp+""); },String.valueOf(i)).start(); } // read for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(()->{ myCache.get(temp+""); },String.valueOf(i)).start(); } } } // With read-write lock class MyCacheLock{ private volatile Map<String,Object> map = new HashMap<>(); // Read / write lock, more fine-grained control! private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // When saving and writing, you only want one thread to write at the same time public void put(String key,Object value){ readWriteLock.writeLock().lock(); // Write lock try { System.out.println(Thread.currentThread().getName()+"write in"+key); map.put(key,value); System.out.println(Thread.currentThread().getName()+"Write complete"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.writeLock().unlock(); } } // When taking and reading, everyone can take and read // Note: if the read is not locked, the thread that may be read when writing will go in!! public void get(String key){ readWriteLock.readLock().lock(); // Read lock try { System.out.println(Thread.currentThread().getName()+"read"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"Read complete"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } } } /** * Custom cache */ class MyCache{ private volatile Map<String,Object> map = new HashMap<>(); // Save, write public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"write in"+key); map.put(key,value); System.out.println(Thread.currentThread().getName()+"Write complete"); } // Take, read public void get(String key){ System.out.println(Thread.currentThread().getName()+"read"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"Read complete"); } }
Operation results:
Summary:
Exclusive lock: that is, the write lock here can only be occupied by one thread at a time!
Shared lock: that is, the read lock here can be occupied by multiple threads at the same time!
A read-write lock is a finer grained lock
10. Blocking queue
block
queue
BlockingQueue is not a new thing!
When do we use blocking queues?
1. Multithreaded concurrent processing
2. Thread pool
Learn to use queues
Add, remove, 4 sets of API s
mode | Throw exception | No exception thrown, with return value | Blocking waiting | Timeout wait |
---|---|---|---|---|
add to | add() | offer() | put() | offer(,) |
remove | remove() | poll() | take() | poll(,) |
Determine queue header element | element() | peek() | ~ | ~ |
/** * Throw exception */ public static void test1(){ // Queue size ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); System.out.println(blockingQueue.add("a")); System.out.println(blockingQueue.add("b")); System.out.println(blockingQueue.add("c")); // IllegalStateException: Queue full, the queue is full, throw an exception! //System.out.println(blockingQueue.add("d")); System.out.println("========================="); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.element()); // View team leader element System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); // java.util.NoSuchElementException has no element, throw an exception! // System.out.println(blockingQueue.remove()); }
/** * No exception thrown, return value! */ public static void test2(){ //Queue size ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); System.out.println(blockingQueue.offer("A")); System.out.println(blockingQueue.offer("B")); System.out.println(blockingQueue.offer("C")); // System.out.println(blockingQueue.offer("D")); // Return false, no exception thrown! System.out.println(blockingQueue.peek()); //Inspection team head element System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); // System.out.println(blockingQueue.poll()); // Return null, no exception thrown! }
/** * Wait, block (always block) */ public static void test3(){ ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); // Always blocked try { blockingQueue.put("a"); blockingQueue.put("b"); blockingQueue.put("c"); // blockingQueue.put("d"); // There is no place in the queue. It has been blocked! System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); // Without this element, it has been blocked! } catch (InterruptedException e) { e.printStackTrace(); } }
/** * Wait, block (timeout wait) */ public static void test4() throws InterruptedException { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); blockingQueue.offer("A"); blockingQueue.offer("B"); blockingQueue.offer("C"); // blockingQueue.offer("D",2, TimeUnit.SECONDS); // Wait more than two seconds to exit! System.out.println("================================"); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); blockingQueue.poll(2,TimeUnit.SECONDS); // More than two seconds! }
SynchronousQueue synchronization queue
Capacity 1
When you enter an element, you must wait for the element to be taken out before you can continue to put elements in it!
put,take
package com.kuang.juc.synbq; import java.util.concurrent.BlockingQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; /** * Synchronization queue * Unlike other blockingqueues, synchronous queues do not store elements * put If you have an element, you must take it out first, otherwise you can't put in the value! */ public class SynchronousQueueDemo { public static void main(String[] args) { BlockingQueue<String> blockingQueue = new SynchronousQueue<String>(); // Synchronization queue new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+" put 1"); blockingQueue.put("1"); System.out.println(Thread.currentThread().getName()+" put 2"); blockingQueue.put("2"); System.out.println(Thread.currentThread().getName()+" put 3"); blockingQueue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } },"T1").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"==>"+blockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"==>"+blockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"==>"+blockingQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } },"T2").start(); } }
Operation results:
be careful:
The synchronization queue is different from other blockingqueues
SynchronousQueue does not store elements
Put an element. You must take it from the inside first, otherwise you can't put in the value!
11. Thread pool (key)
Pool technology
The operation of the program, essence: occupy the resources of the system!
Want to optimize the use of resources! = = > Pool technology
Thread pool, connection pool, memory pool, object pool... Its creation and destruction consume system resources very much!
Pool technology: prepare some resources in advance and put them in the pool. If someone wants to use them, come to me and give them back to me after use!
Benefits of thread pool:
1. Reduce resource consumption
2. Increase the corresponding speed
3. Convenient management
One sentence summary: threads are reused, the maximum number of concurrent threads can be controlled, and it is convenient to manage threads
Thread pool: three methods
Alibaba development manual stipulates:
Three methods of testing
package com.kuang.juc.pool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; // Executors tool class, 3 methods public class Demo01 { public static void main(String[] args) { ExecutorService threadPool = Executors.newSingleThreadExecutor(); // Single thread // ExecutorService threadPool = Executors.newFixedThreadPool(5); // Create a fixed thread pool size // ExecutorService threadPool = Executors.newCachedThreadPool(); // Elastic, strong in case of strength, weak in case of weakness try { for (int i = 1; i <= 100; i++) { // After using thread pool, thread pool is used to create threads threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+ " ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { // When the thread pool runs out, the program ends. Close the thread pool! threadPool.shutdown(); } } }
The so-called three methods are three methods to create a thread pool:
- Executors.newSingleThreadExecutor() / / create the thread pool of a single thread through the tool class
- Executors.newFixedThreadPool(5) / / create a thread pool of size 5 through the tool class
- Executors.newCachedThreadPool() / / created by tool class
7 parameters
Source code analysis:
// 1,Executors.newSingleThreadExecutor() public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } // 2,Executors.newFixedThreadPool(5) public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } // 3,Executors.newCachedThreadPool() public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 2.1 billion OOM 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } // ThreadPoolExecutor(7 parameters) is called essentially public ThreadPoolExecutor(int corePoolSize, // Core thread pool size int maximumPoolSize, // Maximum thread pool size long keepAliveTime, // It will be released if no one calls it after timeout TimeUnit unit, // Timeout unit BlockingQueue<Runnable> workQueue, // Blocking queue ThreadFactory threadFactory, // Thread factory to create thread; Generally don't move RejectedExecutionHandler handler //Reject Policy){ if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
Diagram of 7 major parameters (simulating bank withdrawal)
The above illustrated story shows that when a person goes to the bank to withdraw money, he finds that only two windows are open, which are equivalent to the size of the core thread pool
Then he went to No. 1 to handle business. Another person came in. He saw that window No. 2 was empty and went to handle business,
A third man came. He found that three windows were not open and that someone was handling business in two windows
Just wait on the chair in the waiting area. The fourth person who comes in also waits in the waiting area. The fifth person who comes in also waits in the waiting area
When the waiting area is full at this time, it will trigger the bank to call the salesperson at windows 3, 4 and 5 to hurry back to work
At this time, salesmen 3, 4 and 5 came back
The waiting area here is equivalent to the blocking queue. When the blocking queue is full, the maximum thread pool mechanism will be triggered, that is, windows 3, 4 and 5 and windows 1 and 2 in front together form the maximum thread pool
At this time, the salesperson No. 3, 4 and 5 came back, and the three people in the waiting area went to handle business. At this time, the window is full, reaching the maximum thread pool size
Later, three more people came in and found that the window was full and entered the waiting area
At this time, the window is full and the waiting area is full
When another person suddenly comes in and he has no place to stay, the rejection strategy will be triggered
This person should choose to continue to wait, go back and forth, or drive the person at position 1 away
In the afternoon, there are very few people. There are few people in Windows 1 and 2. Windows 3, 4 and 5 have been empty for two hours, so they close the window first. After work, it is equivalent to a timeout in the thread pool. If no one calls, it will be released (keepAliveTime). The timeout does not wait
Create thread pool manually
// Custom thread pool! new ThreadPoolExecutor() ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy() // When the queue is full, discard the oldest one in the queue and enter the queue instead of his position, and no exception will be thrown! );
4 rejection strategies
package com.kuang.juc.pool; import java.util.concurrent.*; /** * new ThreadPoolExecutor.AbortPolicy() // When the bank is full and someone comes in, don't deal with this person. Throw an exception! * new ThreadPoolExecutor.CallerRunsPolicy() // Where you come from, where you go! I won't accept it. Let the main thread accept it! * new ThreadPoolExecutor.DiscardPolicy() // If the queue is full, no exception will be thrown. Lose the task and do not execute this thread! * new ThreadPoolExecutor.DiscardOldestPolicy() // When the queue is full, discard the oldest one in the queue and enter the queue instead of his position, and no exception will be thrown! */ public class Demo01 { public static void main(String[] args) { // Custom thread pool! new ThreadPoolExecutor() ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy() // When the queue is full, discard the oldest one in the queue and enter the queue instead of his position, and no exception will be thrown! ); try { // Max 8, max load = Deque + max // java.util.concurrent.RejectedExecutionException exceeds the maximum load. Throw an exception! for (int i = 1; i <= 9; i++) { // After using thread pool, thread pool is used to create threads threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+ " ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { // When the thread pool runs out, the program ends. Close the thread pool! threadPool.shutdown(); } } }
Summary of four rejection strategies:
new ThreadPoolExecutor.AbortPolicy() / / when the bank is full and someone else comes in, we don't deal with this person and throw an exception * * (default)**
new ThreadPoolExecutor.CallerRunsPolicy() / / where you come from, where you go! I won't accept it. Let the main thread accept it!
new ThreadPoolExecutor.DiscardPolicy() / / if the queue is full, no exception will be thrown. Lose the task and do not execute this thread!
new ThreadPoolExecutor.DiscardOldestPolicy() / / when the queue is full, discard the oldest one in the queue and enter the queue instead of his position. No exception will be thrown!
Summary and expansion
Question: how to set the maximum size of the thread pool?
Understanding IO intensive and CPU intensive: (tuning)
// How should the maximum thread pool be specified? // 1. CPU intensive, several cores, just a few, can ensure the highest CPU efficiency! // 2. IO intensive > judge the threads that consume IO resources in your program! // Program 15 large tasks io, very resource intensive! System.out.println(Runtime.getRuntime().availableProcessors()); // Get the number of cores of the computer cpu ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, Runtime.getRuntime().availableProcessors(), 3, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy() // When the queue is full, discard the oldest one in the queue and enter the queue instead of his position, and no exception will be thrown! );
12. Four functional interfaces (key points)
Programmers in the new era: lambda expression, chain programming, functional interface, Stream flow computing!
Functional interface: an interface with only one method
@FunctionalInterface public interface Runnable { public abstract void run(); } // Super functional interface // lambda expression, chain programming, functional interface, Stream flow calculation! (jdk8 new features) // Simplify the programming model and apply it in a large number in the new version of the framework! // Foreach (functional interface with parameter of consumer type)
Code test:
1. Function functional interface
package com.kuang.juc.function; import java.util.function.Function; /** * Function Functional interface with one input parameter and one output parameter * As long as it is a functional interface, you can use lambda expression to simplify! */ public class Demo01 { public static void main(String[] args) { // Tool method: output the input value! // Function<String,String> function = new Function<String, String>() { // @Override // public String apply(String str) { // return str; // } // }; Function<String,String> function = (str)->{ return str;}; System.out.println(function.apply("lac")); } }
2. Predicate type interface
package com.kuang.juc.function; import java.util.function.Predicate; /** * Assertive interface: there is an input parameter, and the return value can only be Boolean! */ public class Demo02 { public static void main(String[] args) { // Determine whether the string is empty // Predicate<String> predicate = new Predicate<String>() { // @Override // public boolean test(String str) { // return str.isEmpty(); // } // }; Predicate<String> predicate = (str)->{ return str.isEmpty(); }; System.out.println(predicate.test("")); } }
3. Consumer consumer interface
package com.kuang.juc.function; import java.util.function.Consumer; /** * Consumer interface: only input, no return value! */ public class Demo03 { public static void main(String[] args) { // Consumer<Integer> consumer = new Consumer<Integer>() { // @Override // public void accept(Integer integer) { // System.out.println(integer); // } // }; Consumer<Integer> consumer = (i)->{ System.out.println(i); }; consumer.accept(1024); } }
4. Supplier supply interface
package com.kuang.juc.function; import java.util.function.Supplier; /**, * Supply type interface Supplier: only return values, no parameters */ public class Demo04 { public static void main(String[] args) { // Supplier<String> supplier = new Supplier<String>() { // @Override // public String get() { // return "xx-x"; // } // }; Supplier<String> supplier = ()->{ return "xx-x"; }; System.out.println(supplier.get()); } }
13. Stream flow calculation
What is flow computing
Big data: storage + computing
Storage: the essence of mysql / collection is to store things;
All calculations are left to the flow to calculate!
package com.kuang.juc.stream; import java.util.Arrays; import java.util.List; /** * Topic calculation: this problem can be completed in one minute and can only be realized with one line of code! * Now there are 5 users! Filter: * 1,ID Must be an even number * 2,Age must be greater than 23 * 3,Convert user name to uppercase * 4,User names are sorted alphabetically backwards * 5,Output only one user's name! * 6,Output all information of the user! */ public class Test { public static void main(String[] args) { User u1 = new User(1, "a", 21); User u2 = new User(2, "b", 22); User u3 = new User(3, "c", 23); User u4 = new User(4, "d", 24); User u5 = new User(6, "e", 25); // When converted to a collection, the collection is stored in tubes List<User> list = Arrays.asList(u1, u2, u3, u4, u5); // The calculation is given to the stream // lambda expression, chain programming, functional interface, Stream flow calculation! This question shows incisively and vividly! // 5. Output only one user's name! // list.stream() // .filter((u)->{return u.getId()%2 == 0;}) // .filter((u)->{return u.getAge()>23;}) // .map((u)->{return u.getName().toUpperCase();}) // .sorted((uu1,uu2)->{return uu2.compareTo(uu1);}) // .limit(1) // .forEach(System.out::println); // 6. Output all information of the user! list.stream() .filter(u->{return u.getId()%2 == 0;}) .filter(u->{return u.getAge()>23;}) .map(u->{ u.setName(u.getName().toUpperCase()); return u; }) .sorted((uu1,uu2)->{return uu2.getName().compareTo(uu1.getName());}) .limit(1) .forEach(System.out::println); } }
View the output of 5:
View the output of 6:
14,ForkJoin
Branch merge
What is ForkJoin
ForkJoin executes tasks in parallel in JDK 1.7 and 4! Improve efficiency and large amount of data!
Big data: Mapreduce divides big tasks into small tasks
Forkjoin features: job theft
This one maintains double ended queues!
ForkJoin
package com.kuang.juc.forkjoin; import java.util.concurrent.RecursiveTask; /** * The task of summation calculation! * 3000 6000 (ForkJoin) 9000 (Stream Parallel stream) * How to use forkjoin * 1,ForkJoinPool Through him * 2,Calculate task forkjoinpool execute(ForkJoinTask task) * 3,The calculation class should inherit ForkJoinTask */ public class ForkJoinDemo extends RecursiveTask<Long> { private Long start; // 0 private Long end; // 10_0000_0000 // critical value private Long temp = 10000L; public ForkJoinDemo(Long start, Long end) { this.start = start; this.end = end; } // computing method @Override protected Long compute() { if (end-start<temp){ Long sum = 0L; for (Long i = start; i < end; i++) { sum += i; } return sum; }else { //forkjoin recursion long middle = (start + end)/2; ForkJoinDemo task1 = new ForkJoinDemo(start, middle); task1.fork(); // Split tasks. Push task into thread queue ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end); task2.fork(); // Split tasks. Push task into thread queue return task1.join() + task2.join(); } } }
Test class:
package com.kuang.juc.forkjoin; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.stream.IntStream; import java.util.stream.LongStream; // 3000 6000 (forkjoin) 9000 (stream parallel stream) public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { // test1(); // Time: 5417 // test2(); // Time: 3667 // test3(); // Time: 182 } // Ordinary programmer public static void test1(){ Long sum = 0L; long startTime = System.currentTimeMillis(); for (Long i = 1L; i < 10_0000_0000L; i++) { sum += i; } long endTime = System.currentTimeMillis(); System.out.println("sum=>"+sum+"time: "+(endTime-startTime)); } // ForkJoin public static void test2() throws ExecutionException, InterruptedException { long startTime = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L); //Specific task ForkJoinTask ForkJoinTask<Long> submit = forkJoinPool.submit(task); Long sum = submit.get(); long endTime = System.currentTimeMillis(); System.out.println("sum=>"+sum+"time: "+(endTime-startTime)); } // Stream parallel stream public static void test3(){ long startTime = System.currentTimeMillis(); long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);//(] long endTime = System.currentTimeMillis(); System.out.println("sum=>"+sum+"time: "+(endTime-startTime)); } }
ForkJoin usage scenario: use in a large amount of data! A small amount of data is not necessary!
15. Asynchronous callback
The original intention of Future design: to model the result of an event in the Future
package com.kuang.juc.future; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Call Ajax asynchronously * // Asynchronous execution * // Successful callback * // Failed callback * */ public class Demo01 { public static void main(String[] args) throws ExecutionException, InterruptedException { // Asynchronous callback with no return value // CompletableFuture<Void> completableFuture = CompletableFuture. Runasync (() - > {/ / executes an asynchronous task without a return value // try { // TimeUnit.SECONDS.sleep(2); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+"runAsync=>Void"); // }); // // System.out.println("0V0"); // System.out.println(completableFuture.get()); // Asynchronous callback with return value // ajax successful and failed callbacks // The error message is returned CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { //Execute asynchronous task with return value System.out.println(Thread.currentThread().getName()+"=>supplyAsync=>String"); int i = 10/0; return "Shatter the darkness with thunder"; }); // Compilation succeeded! System.out.println(completableFuture.whenComplete((t, u) -> { System.out.println("t=>" + t); // t represents the normal return result System.out.println("u=>" + u); // u stands for error message }).exceptionally((e) -> { //Compilation failed System.out.println(e.getMessage()); return "Two cups"; }).get()); } }
Check the result: no exception, correct output result:
If there is an exception in the program, run it again:
16,JMM
Please talk about your understanding of Volatile
Volatile is a lightweight synchronization mechanism provided by Java virtual machine
1. Ensure visibility
2. Atomicity is not guaranteed
3. Prohibit instruction rearrangement
What is JMM
JMM: it's a Java memory model. It doesn't exist. It's a concept and model!
Some synchronization conventions of JMM:
1. Before the thread is unlocked, the shared variables of the thread must be flushed back to main memory immediately
2. Before a thread locks, it must read the latest value in the main memory into its own working memory
3. Locking and unlocking are the same lock
Thread working memory main memory
8 operations in JMM
There are 8 kinds of memory interactive operations. The virtual machine implementation must ensure that each operation is atomic and cannot be separated (for variables of double and long types, exceptions are allowed for load, store, read and write operations on some platforms)
- lock: a variable that acts on main memory and identifies a variable as thread exclusive
- unlock: a variable that acts on the main memory. It releases a locked variable, and the released variable can be locked by other threads
- read: acts on the main memory variable. It transfers the value of a variable from the main memory to the working memory of the thread for subsequent load actions
- load: a variable that acts on working memory. It puts the read operation from main memory into working memory
- Use: acts on variables in working memory. It transfers variables in working memory to the execution engine. Whenever the virtual machine encounters a value that needs to be used, it will use this instruction
- assign: acts on a variable in working memory. It puts a value received from the execution engine into the variable copy in working memory
- store: a variable that acts on main memory. It transfers the value of a variable from working memory to main memory for subsequent write
- write: acts on a variable in main memory. It puts the value of the variable obtained from the working memory by the store operation into the variable in main memory
JMM formulates the following rules for the use of these eight instructions:
- One of read and load, store and write operations is not allowed to appear alone. That is, read must be loaded and store must be written
- The thread is not allowed to discard its latest assign operation, that is, after the data of the work variable has changed, it must inform the main memory
- A thread is not allowed to synchronize data without assign from working memory back to main memory
- A new variable must be born in main memory. Working memory is not allowed to directly use an uninitialized variable. This means that the assign and load operations must be performed before the use and store operations are performed on the linked variables
- Only one thread can lock a variable at a time. After multiple locks, you must perform the same number of unlocks to unlock
- If you lock a variable, the value of this variable in all working memory will be cleared. Before the execution engine uses this variable, you must re load or assign to initialize the value of the variable
- If a variable is not locked, it cannot be unlocked. You cannot unlock a variable that is locked by another thread
- Before unlock ing a variable, you must synchronize the variable back to main memory
Problem: the program does not know that the value in main memory has changed
17,Volatile
1. Ensure visibility
package com.kuang.juc.Tvolatile; import java.util.concurrent.TimeUnit; public class JMMDemo { // Without volatile, the program will loop // Visibility can be guaranteed! private volatile static int num = 0; public static void main(String[] args) { //main new Thread(()->{ //Thread 1 does not know the change of main memory! while (num==0){ } },"1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } num = 1; System.out.println(num); } }
2. Atomicity is not guaranteed
Atomicity: indivisible
Thread A cannot be disturbed when executing tasks. It either succeeds or fails at the same time!
package com.kuang.juc.Tvolatile; // Atomicity is not guaranteed public class VDemo02 { // volatile does not guarantee atomicity! private volatile static int num = 0; public static void add(){ num++; } public static void main(String[] args) { // Theoretically, the output should be 2w for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ //main gc // Comity, re compete for resources! Thread.yield(); } System.out.println(Thread.currentThread().getName()+" "+num); } }
The output is less than 2w, there is a problem!
Question: how to ensure atomicity without synchonized and lock?
Use atomic classes to solve atomic problems
package com.kuang.juc.Tvolatile; import java.util.concurrent.atomic.AtomicInteger; // Atomicity is not guaranteed public class VDemo02 { private static AtomicInteger num = new AtomicInteger(); //Integer of atomic class public static void add(){ num.getAndIncrement(); // AtomicInteger +1 method, CAS used at the bottom } public static void main(String[] args) { // Theoretically, the output should be 2w for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ //main gc // Comity, re compete for resources! Thread.yield(); } System.out.println(Thread.currentThread().getName()+" "+num); } }
View run results:
The bottom of these classes are directly linked to the operating system! Modify the value directly in memory!
Unsafe class is a very special existence!
Instruction rearrangement
What is instruction rearrangement: the program you write is not executed by the computer in the order you write!
Source code - > compiler optimization rearrangement – > instruction parallelism may also rearrange – > memory system may also rearrange – > execution
When the processor executes instruction rearrangement, it considers the dependence between data
int x = 1; //1 int y = 2; //2 x = x + 5; //3 y = x * x; //4 //What we expect: 1234 //When the program is executed, it may be 1324 2134 2413 //Could it be: 4123
Possible impact results: the default values of premise a, b, x and y are 0
Thread A | Thread B |
---|---|
x=a | y=b |
b=1 | a=2 |
Normal results: x = 0, y = 0;
If an instruction rearrangement occurs:
Thread A | Thread B |
---|---|
b=1 | a=2 |
x=a | y=b |
The instruction rearrangement has a strange result: x=2, y=1;
Instruction rearrangement understanding
Adding volatile keyword can avoid instruction rearrangement!
Memory barrier. That is, CPU instructions. Its role:
1. Ensure the execution sequence of specific operations!
2. Memory visibility of some variables can be guaranteed (with these features, volatile achieves visibility)
Volatile can maintain visibility and cannot guarantee atomicity. Due to memory barrier, it can avoid instruction rearrangement
18. Completely play with singleton mode
Hungry man, DCL lazy man, deep study!
Hungry Han style
package com.kuang.juc.single; // Hungry Han style single case public class Hungry { // It may waste space private byte[] data1 = new byte[1024*1024]; private byte[] data2 = new byte[1024*1024]; private byte[] data3 = new byte[1024*1024]; private byte[] data4 = new byte[1024*1024]; private Hungry(){ } private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance(){ return HUNGRY; } }
DCL lazy
package com.kuang.juc.single; import com.sun.org.apache.regexp.internal.RE; import java.lang.reflect.Constructor; import java.lang.reflect.Field; // Lazy singleton mode public class LazyMan { // Flag bit private static boolean panghu = false; // Lock to prevent Decompilation private LazyMan(){ synchronized (LazyMan.class){ if (panghu==false){ panghu = true; }else { throw new RuntimeException("Do not attempt to use reflection to break exceptions"); } } } private volatile static LazyMan lazyMan; // Avoid instruction rearrangement // Lazy single case DCL with dual detection lock mode private static LazyMan getInstance(){ if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); // Not atomic operation /** * 1,Allocate memory space * 2,Execute the construction method and initialize the object * 3,Point this object to this space * * 123 * 132 A * B //At this time, LazgyMan has not completed the construction */ } } } return lazyMan; } //Reflection! public static void main(String[] args) throws Exception { // 1. Use reflection destruction to solve the problem of private structure and locking // LazyMan instance1 = LazyMan.getInstance(); // //1. Get reflection object // Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); // declaredConstructor.setAccessible(true); // Ignore private constructors // LazyMan instance2 = declaredConstructor.newInstance(); // Create objects by reflection // // System.out.println(instance1); // System.out.println(instance2); // 2. Directly create two objects through reflection and add flag bit to solve! // Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); // declaredConstructor.setAccessible(true); // Ignore private constructors // LazyMan instance1 = declaredConstructor.newInstance(); // Create objects by reflection // LazyMan instance2 = declaredConstructor.newInstance(); // Create objects by reflection // // System.out.println(instance1); // System.out.println(instance2); // 3. If the flag bit is decompiled, you know, it is cracked again Field panghu = LazyMan.class.getDeclaredField("panghu"); panghu.setAccessible(true); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); //Ignore private constructors LazyMan instance1 = declaredConstructor.newInstance(); //Create objects by reflection panghu.set(instance1,false); LazyMan instance2 = declaredConstructor.newInstance(); //Create objects by reflection System.out.println(instance1); System.out.println(instance2); } } // Double check lock mode description: // This method first determines whether the variable is initialized or not, and then obtains the lock. // After obtaining the lock, judge whether the variable is initialized again. // The purpose of the second judgment is that it is possible that other threads have obtained locks and initialized variables. The variables will not be initialized until the second check is passed. // This method checks and determines twice and uses the lock, so the image is called double check lock mode.
Static inner class
package com.kuang.juc.single; //Static memory class public class Holder { private Holder(){ } public static Holder getInstance(){ return InnerClass.holder; } public static class InnerClass{ private static final Holder holder = new Holder(); } }
Single case is unsafe and there is reflection
Enumeration is on the stage
package com.kuang.juc.single; import java.lang.reflect.Constructor; // Enumeration itself is also a class class public enum EnumSingle { INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } } class Test{ // Attempt to crack enumeration through reflection public static void main(String[] args) throws Exception { EnumSingle instance1 = EnumSingle.INSTANCE; Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class); declaredConstructor.setAccessible(true); EnumSingle instance2 = declaredConstructor.newInstance(); // Appears: Java lang.NoSuchMethodException: com. kuang. juc. single. EnumSingle.< Init > () has no empty argument constructor System.out.println(instance1); System.out.println(instance2); } }
It is not a parameterless structure, but a parameterless structure with two parameters!
Through decompilation, it was found that it deceived us
Use JAD Exe to decompile
**The final decompiled source code of enumeration types: * * it is found that she has a two parameter construction method, not an empty parameter construction method
package com.kuang.juc.single; public final class EnumSingle extends Enum { public static EnumSingle[] values() { return (EnumSingle[])$VALUES.clone(); } public static EnumSingle valueOf(String name) { return (EnumSingle)Enum.valueOf(com/kuang/juc/single/EnumSingle, name); } private EnumSingle(String s, int i) { super(s, i); } public EnumSingle getInstance() { return INSTANCE; } public static final EnumSingle INSTANCE; private static final EnumSingle $VALUES[]; static { INSTANCE = new EnumSingle("INSTANCE", 0); $VALUES = (new EnumSingle[] { INSTANCE }); } }
Again, through the two parameter construction method, the reflection attempts to crack, and it is found that:
19. Deep understanding of CAS
What is CAS
package com.kuang.juc.cas; import java.util.concurrent.atomic.AtomicInteger; public class CASDemo { // CAS compareandset: compare and exchange! public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2020); // Expectations, updates // public final boolean compareAndSet(int expect, int update) // If my expected value is reached, update it, otherwise don't update it! CAS is the concurrency primitive of CPU!! System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); } }
Unsafe class
CAS: compare the value in the current working memory with the value in the main memory. If this value is expected, execute the operation! If not, it will cycle all the time (the bottom layer is spin lock)
Disadvantages:
1. The cycle takes time
2. One time can only guarantee the atomicity of one shared variable
3. ABA problem
CAS: ABA problem (civet cat for Prince)
package com.kuang.juc.cas; import java.util.concurrent.atomic.AtomicInteger; public class CASDemo { // CAS compareandset: compare and exchange! public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2020); // For the sql we usually write: optimistic lock! // Expectations, updates // public final boolean compareAndSet(int expect, int update) // If my expected value is reached, update it, otherwise don't update it! CAS is the concurrency primitive of CPU!! // =====================Troublemaker thread=========================== System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2021, 2020)); System.out.println(atomicInteger.get()); // =====================Expected thread=========================== System.out.println(atomicInteger.compareAndSet(2020, 6666)); System.out.println(atomicInteger.get()); } }
How to solve this problem: using atomic references
20. Atomic reference
Solve the ABA problem and introduce atomic reference! Corresponding thought optimistic lock!
Atomic operation with version number! (equivalent to optimistic lock in sql)
be careful:
package com.kuang.juc.cas; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicStampedReference; public class CASDemo { // CAS compareandset: compare and exchange! public static void main(String[] args) { // Pit: AtomicStampedReference // Note: if the generic is a wrapper class, pay attention to the reference of the object! // For normal business operations, objects are compared one by one! AtomicStampedReference<Integer> atomicReference = new AtomicStampedReference<Integer>(1,1); new Thread(()->{ int stamp = atomicReference.getStamp(); //Get the initial version number 1 System.out.println("a1=>"+stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // version + 1 System.out.println(atomicReference.compareAndSet(1, 2, atomicReference.getStamp(), atomicReference.getStamp()+1)); System.out.println("a2=>"+ atomicReference.getStamp()); System.out.println(atomicReference.compareAndSet(2, 1, atomicReference.getStamp(), atomicReference.getStamp()+1)); System.out.println("a3=>"+ atomicReference.getStamp()); },"a").start(); // Same principle as optimistic lock new Thread(()->{ int stamp = atomicReference.getStamp(); //Get the original version number 1 System.out.println("b1=>"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicReference.compareAndSet(1, 6, stamp, stamp + 1)); System.out.println("b2=>"+atomicReference.getStamp()); },"b").start(); } }
View results:
21. Understanding of various locks
1. Fair lock, unfair lock
Fair lock: very fair. You can't jump the queue. You must come first!
Unfair lock: it's very unfair. You can jump the queue (all unfair 3h 3s by default) to ensure efficiency!
public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
2. Reentrant lock
Reentrant lock (recursive lock)
Synchonized version reentrant lock
package com.kuang.juc.lock; import java.util.concurrent.TimeUnit; /** * Synchonized Version of reentrant lock */ public class Demo01 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ phone.sms(); },"A").start(); new Thread(()->{ phone.sms(); },"B").start(); } } class Phone{ public synchronized void sms(){ System.out.println(Thread.currentThread().getName()+" sms"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } call(); // There are locks here, too } public synchronized void call(){ System.out.println(Thread.currentThread().getName()+" call"); } }
Lock version reentrant lock
package com.kuang.juc.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Demo02 { public static void main(String[] args) { Phone2 phone = new Phone2(); new Thread(()->{ phone.sms(); },"A").start(); new Thread(()->{ phone.sms(); },"B").start(); } } class Phone2{ Lock lock = new ReentrantLock(); public void sms(){ lock.lock();// Details: lock lock(); lock. unlock(); // lock locks must be paired, or they will die inside try { System.out.println(Thread.currentThread().getName()+" sms"); TimeUnit.SECONDS.sleep(3); call(); // There are locks here, too } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void call(){ lock.lock(); // Details: lock lock(); lock. unlock(); try { System.out.println(Thread.currentThread().getName()+" call"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
summary
Reentrant lock means that a thread has obtained a lock and can obtain the lock again without deadlock!
3. Spin lock
What is spin lock
Let's define a spin lock ourselves
package com.kuang.juc.lock; import java.util.concurrent.atomic.AtomicReference; /** * Custom spin lock */ public class Spinlock { // int defaults to 0 // Thread defaults to null AtomicReference<Thread> atomicReference = new AtomicReference<>(); // Lock public void mylock(){ Thread thread = Thread.currentThread(); System.out.println(thread.getName()+"===> mylock"); // Spin lock while (!atomicReference.compareAndSet(null,thread)){ } } // Unlock public void myUnlock(){ Thread thread = Thread.currentThread(); System.out.println(thread.getName()+"===> myUnlock"); // Spin lock atomicReference.compareAndSet(thread,null); } }
Custom spin lock for testing:
package com.kuang.juc.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestSpinlock { public static void main(String[] args) throws InterruptedException { // Lock lock = new ReentrantLock(); // lock.lock(); // lock.unlock(); // Use custom spin lock to realize CAS at the bottom Spinlock spinlock = new Spinlock(); new Thread(()->{ spinlock.mylock(); try { TimeUnit.SECONDS.sleep(5); } catch (Exception e) { e.printStackTrace(); } finally { spinlock.myUnlock(); } },"T1").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ spinlock.mylock(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { e.printStackTrace(); } finally { spinlock.myUnlock(); } },"T2").start(); } }
result:
explain:
1. The T1 above gets the lock first. After getting the lock, judge that the Thread object is empty and change the object into the current Thread object without spin
2. T2 Thread comes in. The Thread object is not empty. When the while loop condition is true, T2 is always in the spin state
3. After T1 unlocks, T2 finds that the Thread object is reset to null. When the while condition is not met, the spin ends, and T2 unlocks again!
4. Deadlock
What is a deadlock
Deadlock test, how to eliminate deadlock!
package com.kuang.juc.lock; import java.util.concurrent.TimeUnit; public class DeadLockDemo { public static void main(String[] args) { String LockA="A"; String LockB="B"; new Thread(new MyThread(LockA,LockB),"thread a").start(); new Thread(new MyThread(LockB,LockA),"thread b").start(); } } class MyThread implements Runnable{ private String LockA; private String LockB; public MyThread(String lockA, String lockB) { LockA = lockA; LockB = lockB; } @Override public void run() { synchronized (LockA){ System.out.println(Thread.currentThread().getName()+" Lock:" +LockA+"Trying to get"+LockB); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (LockB){ System.out.println(Thread.currentThread().getName()+" Lock:" +LockB+"Trying to get"+LockA); } } } }
result:
A deadlock has occurred
reason:
1. LockA and LockB in the constant pool are locked
2. When thread a comes in, it assigns a value to it. After the assignment, LockA=A and LockB=B in the constant pool are locked. When LockA=A in thread a, it gets the lock of a and tries to get the lock of B
3. After thread B comes in, LockA=B in thread B gets the lock of B and tries to get the lock of A
4. This is a deadlock!
solve the problem
1. Use jps -l to locate the process number
2. Use jstack process number to view the stack information of Deadlock:
Troubleshooting during interview or work:
1. Look at the log 9
2. Look at the stack information! one
explain
This course is from the JUC of the crazy talk java series: https://www.bilibili.com/video/BV1B7411L7tE The course is of high quality. Welcome to study.
I wrote this note one by one in class