11. Thread pool (key)
Thread pool: 3 methods, 7 parameters and 4 rejection strategies
Pool technology
The operation of the program, essence: occupy the resources of the system! (optimize the use of resources = > pooling Technology)
Create and destroy thread pool, connection pool, memory pool and object pool. It is a waste of resources.
Pooling Technology: prepare some resources in advance. If someone wants to use them, come to me and give them back to me after use.
Benefits of thread pooling
- Reduce the consumption of system resources
- Improve response speed
- Convenient management
Thread reuse, can control the maximum number of concurrent threads, and manage threads
Thread pool: 3 methods
package juc.poolIO; import java.util.concurrent.*; 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(); // Scalable, strong in case of strength, weak in case of weakness ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 3 , TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); //When the queue is full, try to compete with the earliest (fail to discard), and no exception will be thrown //new ThreadPoolExecutor.DiscardPolicy()); // If the queue is full, the task will be lost and no exception will be thrown //new ThreadPoolExecutor.CallerRunsPolicy()); // Where did you come from? Where did you go //new ThreadPoolExecutor.AbortPolicy()); // Full, no processing, exception thrown try { //Maximum load: queue + max for(int i = 0; i < 20; i++){ 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(); } } }
Thread pool: 7 parameters
package juc.poolIO; import java.util.concurrent.*; public class IO { public static void main(String[] args) { //Custom thread pool! Working ThreadPoolExecutor //How to set the maximum size of the pool //How should the maximum thread be defined //1. cpu intensive cores are the ones that can maintain the highest cpu efficiency //2. IO intensive > judge the IO intensive threads in your program // Program: 15 large tasks are very resource consuming. Set the maximum number of threads to 30 ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 3 , TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); //When the queue is full, try to compete with the earliest (fail to discard), and no exception will be thrown //new ThreadPoolExecutor.DiscardPolicy()); // If the queue is full, the task will be lost and no exception will be thrown //new ThreadPoolExecutor.CallerRunsPolicy()); // Where did you come from? Where did you go //new ThreadPoolExecutor.AbortPolicy()); // Full, no processing, exception thrown } }
Manually create a thread pool
package juc.poolIO; import java.util.concurrent.*; 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(); // Scalable, strong in case of strength, weak in case of weakness ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 3 , TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); //When the queue is full, try to compete with the earliest (fail to discard), and no exception will be thrown //new ThreadPoolExecutor.DiscardPolicy()); // If the queue is full, the task will be lost and no exception will be thrown //new ThreadPoolExecutor.CallerRunsPolicy()); // Where did you come from? Where did you go //new ThreadPoolExecutor.AbortPolicy()); // Full, no processing, exception thrown try { //Maximum load: queue + max for(int i = 0; i < 20; i++){ 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(); } } }
Thread pool: four rejection strategies
new ThreadPoolExecutor.DiscardOldestPolicy()); //When the queue is full, try to compete with the earliest (fail to discard), and no exception will be thrown new ThreadPoolExecutor.DiscardPolicy()); //If the queue is full, the task will be lost and no exception will be thrown new ThreadPoolExecutor.CallerRunsPolicy()); //Where did you come from? Where did you go new ThreadPoolExecutor.AbortPolicy()); //Full, no processing, exception thrown
How to set the maximum capacity of the pool
IO intensive, CPU intensive: (tuning)
package juc.poolIO; import java.util.concurrent.*; public class IO { public static void main(String[] args) { //Custom thread pool! Working ThreadPoolExecutor //How to set the maximum size of the pool //How should the maximum thread be defined //1. cpu intensive cores are the ones that can maintain the highest cpu efficiency //2. IO intensive > judge the IO intensive threads in your program // Program: 15 large tasks are very resource consuming. Set the maximum number of threads to 30 ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 3 , TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); //When the queue is full, try to compete with the earliest (fail to discard), and no exception will be thrown //new ThreadPoolExecutor.DiscardPolicy()); // If the queue is full, the task will be lost and no exception will be thrown //new ThreadPoolExecutor.CallerRunsPolicy()); // Where did you come from? Where did you go //new ThreadPoolExecutor.AbortPolicy()); // Full, no processing, exception thrown } }
12. Four functional interfaces
lambda expression, chain programming, functional interface, Stream flow calculation
Functional interface: an interface with only one method
Four functional interfaces
Function functional interface
// Function part of the source code public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t);
package juc.function; import java.util.function.Function; /** * Function Functional interface with one input parameter and one output * As long as it is a functional interface, it can be simplified with lambda expressions */ public class Demo01 { public static void main(String[] args) { // Function<String, String> f = new Function<String, String>() { // @Override // public String apply(String str) { // return str; // } // }; Function<String, String> f = (str) -> {return str;}; System.out.println(f.apply("aaa")); } }
Predict interface
Assertive interface: there is an input parameter, and the return value can only be Boolean
package 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 //The Predicate return type is true/false // Predicate<String> p = new Predicate<String>() { // @Override // public boolean test(String str) { // return str.isEmpty(); // } // }; Predicate<String> p = a -> {return a.isEmpty();}; System.out.println(p.test("sss")); } }
Consumer consumer interface
Consumer interface: only input, no return value
package juc.function; import java.util.function.Consumer; /** * Consumer Consumer interface: only input, no return value */ public class Demo03 { public static void main(String[] args) { // Consumer<String> c = new Consumer<String>(){ // @Override // public void accept(String str){ // System.out.println(str); // } // }; Consumer<String> c = (str) -> {System.out.println(str);}; c.accept("cc"); } }
Supplier supply interface
Supply type interface: no input (parameter), only return value
package juc.function; import java.util.function.Supplier; /** * Supplier The supply interface has no parameters, only return values */ public class Demo04 { public static void main(String[] args) { // Supplier<Integer> s = new Supplier<Integer>() { // @Override // public Integer get() { // return 1024; // } // }; Supplier<Integer> s = () -> {return 1024;}; System.out.println(s.get()); } }
13. Stream flow calculation
# What is Stream computing Big data: storage + calculation Set MySQL The essence is to store things All calculations should be left to the flow
package juc.Stream; import java.util.Arrays; import java.util.List; import java.util.Locale; /** * Title Requirements: complete this title in one minute, which can only be realized with one line of code! * Now there are 5 users! Filter: * 1. ID Must be even * 2. Age must be greater than 23 * 3. User name to uppercase * 4. User names are sorted alphabetically backwards * 5. Output only one 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); //A collection is storage List<User> list = Arrays.asList(u1, u2, u3, u4, u5); //The calculation is given to the Stream/* 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); } }
14. ForkJoin
# What is ForkJoin ForkJoin stay JDK1.7,Execute tasks in parallel, improve data and large amount of data. big data: Map Reduce(Break big tasks into small ones)
ForkJoin features: job theft
Maintained is a dual ended queue
ForkJoiin
package juc.forkjoin; import java.util.concurrent.RecursiveAction; import java.util.concurrent.RecursiveTask; /** * How to use forkjoin * 1. forkjoinPool Execute through it * 2. Calculate the task forkjoinpool execute(ForkJoinTask task) * 3. The calculation class should inherit ForkJoinTask */ public class ForkJoinDemo extends RecursiveTask<Long> { private Long start; // 1 private Long end; // 1990900000 //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; } System.out.println(sum); }else{//forkjoin //Branch merge calculation forkjoin Long mid = (start + end) / 2; ForkJoinDemo task1 = new ForkJoinDemo(start, mid); task1.fork(); //Split the task and push the task into the thread queue ForkJoinDemo task2 = new ForkJoinDemo(mid + 1, end); task2.fork(); //Split the task and push the task into the thread queue return task1.join() + task2.join(); } return null; } }
package juc.forkjoin; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.stream.LongStream; public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { test3(); } public static void test1(){ Long sum = 0L; long start = System.currentTimeMillis(); for(Long i = 1L; i <= 10_0000_0000; i++){ sum += i; } long end = System.currentTimeMillis(); System.out.println("sum=" + sum + "Time:" + (end - start)); } public static void test2() throws ExecutionException, InterruptedException { long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinDemo(1L, 10_0000_0000L); ForkJoinTask<Long> submit = forkJoinPool.submit(task); //Submit task Long sum = submit.get(); long end = System.currentTimeMillis(); System.out.println("sum=" + "Time:" + (end - start)); } public static void test3(){ long start = System.currentTimeMillis(); //Stream parallel stream () (] long sum = LongStream.rangeClosed(0L, 10_0000_0000).parallel().reduce(0, Long::sum); long end = System.currentTimeMillis(); System.out.println("sum=" + "Time:" + (end - start)); } }
15. Asynchronous callback
# Asynchronous callback Imagine a scenario, A Is a step in processing business, A A problem needs to be solved at this time A Can ask B,Give Way B To tell A Answer, during this period, A You can continue to do your own things without because B Blocked by what you do. So we thought of giving B Set a thread so that B To deal with time-consuming operations, and then tell the results after processing A. So the point of this problem is B After processing How to tell the results A. We can go directly to A Write a method pair in B The processed results are processed, and then B Call after processing A This method. such A call B To deal with the process, B call A of C Method to process the result is called a callback.
package juc.future; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** * Asynchronous call: completable future * // Asynchronous execution * // Successful callback * // Failed callback */ public class Demo01 { public static void main(String[] args) throws ExecutionException, InterruptedException { // //runAsync asynchronous callback with no return value // CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> { // try { // TimeUnit.SECONDS.sleep(2); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() + "runAsync=>Void"); // }); // // System.out.println("1111"); // completableFuture.get(); // Get blocking execution results //supplyAsync asynchronous callback with return value //ajax successful and failed callbacks //Return error message CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer"); int i = 10 / 0; return 1024; }); completableFuture.whenComplete((t, u) -> { System.out.println("t=>" + t); //Normal return result System.out.println("u=>" + u); // Error message: Java util. concurrent. CompletionException: java. lang.ArithmeticException: / by zero }).exceptionally( (e) -> { System.out.println(e.getMessage()); return 233; //You can get the return result of the error }); /** * success Code 200 * error Code 404 */ } }
16. JMM
# Understanding of Volatile 1. Ensure visibility 2. Atomicity is not guaranteed 3. Prevent instruction rearrangement # What is JMM JMM: Java Memory model, nonexistent things, concepts, conventions # Some synchronization conventions about JMM 1. Before the thread is unlocked, the shared variable must be flushed back to main memory immediately. 2. Before locking a thread, the latest value in main memory must be read into working memory. 3. Locking and unlocking are the same lock.
Thread working memory, main memory
8 operations:
There are eight kinds of memory interaction operations. The virtual machine must ensure that each operation is atomic and inseparable (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 8 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 of main memory has been modified
17. Volatile
1. Ensure visibility
package juc.cvolatile; import java.util.concurrent.TimeUnit; public class JMMDemo { // Without volatile, the program will loop // Adding volatile ensures visibility private volatile static int number = 0; public static void main(String[] args) { //main thread new Thread(() -> { while(number == 0){ } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } number = 1; System.out.println(number); } }
Atomicity is not guaranteed
Atomicity: indivisible
Thread A cannot be disturbed or divided when executing tasks. It either succeeds or fails at the same time.
package juc.cvolatile; import java.util.concurrent.atomic.AtomicInteger; // volatile does not guarantee atomicity public class VDDemo02 { // volatile does not guarantee atomicity // Integer of atomic class private volatile static AtomicInteger number = new AtomicInteger(); private static void add(){ //number++; // Not an atomic operation number.getAndIncrement(); // AtomicInteger + 1 method, CAS } public static void main(String[] args) { // Theoretically, the final num should be 20000 for(int i = 0; i < 20; i++){ new Thread(() -> { for(int j = 0; j < 10000; j++){ add(); } }).start(); } while (Thread.activeCount() > 2){ //main gc } System.out.println(Thread.currentThread().getName() + " " + number); } }
If Lock and Synchronized are not added, atomic classes can be used to ensure atomicity
package juc.cvolatile; import java.util.concurrent.atomic.AtomicInteger; // volatile does not guarantee atomicity public class VDDemo02 { // volatile does not guarantee atomicity // Integer of atomic class private volatile static AtomicInteger number = new AtomicInteger(); private static void add(){ //number++; // Not an atomic operation number.getAndIncrement(); // AtomicInteger + 1 method, CAS } public static void main(String[] args) { // Theoretically, the final num should be 20000 for(int i = 0; i < 20; i++){ new Thread(() -> { for(int j = 0; j < 10000; j++){ add(); } }).start(); } while (Thread.activeCount() > 2){ //main gc } System.out.println(Thread.currentThread().getName() + " " + number); } }
Instruction rearrangement
What is instruction rearrangement?: The computer does not execute the program we write as you write.
Source code - > compiler optimized rearrangement - > instruction parallelism may also rearrange - > memory system may also rearrange - > execution
When the processor performs instruction rearrangement, it will consider the dependency between data
int x = 1; // 1 int y = 2; // 2 x = x + 5; // 3 y = x * x; // 4
What we expect: 1234, but it may become 2134 or 1324 when executed
But it can't be 4123!
Premise: the four values of a, B, x, y are all 0 by default:
May cause impact and get different results:
Thread A | Thread B |
---|---|
x = a | y = b |
b =1 | a = 2 |
Normal results: x = 0; y = 0; However, the following results may occur due to instruction rearrangement:
Thread A | Thread B |
---|---|
b = 1 | a = 2 |
x = a | y = b |
Weird result caused by instruction rearrangement: x = 2; y = 1;
How to solve:
volatile avoids instruction rearrangement:
Memory barrier. CPU instructions. effect:
- Ensure the execution sequence of specific operations!
- Memory visibility of some variables can be guaranteed (visibility is achieved by volatile using these features)
volatile ensures visibility. Atomicity cannot be guaranteed. Due to the memory barrier, instruction rearrangement can be avoided!
volatile memory barrier is used most in singleton mode!
18. Singleton mode
Hungry, DCL lazy
Hungry Han style
package 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(); private static Hungry getInstance(){ return HUNGRY; } }
DCL lazy
package juc.single; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; // Lazy style public class LazyMan { private static boolean qinjiang = false; private LazyMan(){ synchronized (LazyMan.class){ if(qinjiang == false){ qinjiang = true; } else { throw new RuntimeException("Do not attempt to break exceptions by reflection"); } } System.out.println(Thread.currentThread().getName() + "ok"); } private volatile static LazyMan lazyMan; // Lazy DCL with dual detection lock mode public static LazyMan getInstance(){ if(lazyMan == null){ synchronized (LazyMan.class){ if(lazyMan == null){ lazyMan = new LazyMan(); //Not an atomic operation /** * 1. Allocate memory space * 2. Execute the construction method to initialize the object * 3. Point this object to this space * * * 123 * 132 A * B //At this time, the lazyman has not been constructed */ } } } return lazyMan; } /* //Multithreading concurrency public static void main(String[] args) { for(int i = 0; i < 10; i++){ new Thread(() -> { LazyMan.getInstance(); }).start(); } } */ // reflex public static void main(String[] args) throws Exception{ Field qinjiang = LazyMan.class.getDeclaredField("qinjiang"); qinjiang.setAccessible(true); //LazyMan instance = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(); declaredConstructor.setAccessible(true); LazyMan instance = declaredConstructor.newInstance(); qinjiang.set(instance, false); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance); System.out.println(instance2); } }
Static inner class
package juc.single; public class Holder { private Holder(){ } public static Holder getInstance(){ return InnerClass.HOLDER; } public static class InnerClass{ private static Holder HOLDER = new Holder(); } }
Singleton is not safe because reflection can destroy singleton
// Solution private LazyMan(){ synchronized (LazyMan.class){ if (csp == false){ csp = true; }else { throw new RuntimeException("Do not attempt to use reflection to break exceptions"); } } }
enumeration
package juc.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; // What is enum? It is also a Class public enum EnumSingle { INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } } class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { EnumSingle instance1 = EnumSingle.INSTANCE; Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class); declaredConstructor.setAccessible(true); EnumSingle instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }
19. In depth understanding of CAS
CAS
# What is CAS CAS yes CompareAndSwap(It can also be CompareAndSet)The abbreviation of, literally, is to compare and update, To put it simply: take a value from a memory V,And expected values A Compare if memory value V And expected values A The results are equal, then We'll take the new value B Update to memory. If it is not equal, repeat the above operation until it is successful,
package 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! //Expectations, updates public final boolean compareAndSet(int expect, int update) If my expected value is reached, I will update it. Otherwise, I will not update it ==========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, 2021)); System.out.println(atomicInteger.get()); } }
ABA problem
# What is the ABA problem A thread sends data A Become B,Then it became again A. When another thread reads, find A If there is no change, it is mistaken for the original one A. This is the famous ABA Question.
package 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) { //AtomicInteger atomicInteger = new AtomicInteger(2020); //AtomicStampedReference: if the generic type is a wrapper class, pay attention to the reference of the object AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1); new Thread(() -> { int stamp = atomicStampedReference.getStamp(); //Get version number System.out.println("a1=>" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1)); System.out.println("a2=>" + atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet(2, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1)); System.out.println("a3=>" + atomicStampedReference.getStamp()); }, "a").start(); new Thread( () -> { int stamp = atomicStampedReference.getStamp(); //Get version number System.out.println("b1=>" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1)); System.out.println("b1=>" + atomicStampedReference.getStamp()); }, "b").start(); } }
20. Atomic reference
Solve the ABA problem and introduce atomic reference! Corresponding thought: optimistic lock!
Atomic operation with version number!
package 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) { //AtomicInteger atomicInteger = new AtomicInteger(2020); //AtomicStampedReference: if the generic type is a wrapper class, pay attention to the reference of the object AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1); new Thread(() -> { int stamp = atomicStampedReference.getStamp(); //Get version number System.out.println("a1=>" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1)); System.out.println("a2=>" + atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet(2, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1)); System.out.println("a3=>" + atomicStampedReference.getStamp()); }, "a").start(); new Thread( () -> { int stamp = atomicStampedReference.getStamp(); //Get version number System.out.println("b1=>" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1)); System.out.println("b1=>" + atomicStampedReference.getStamp()); }, "b").start(); } }
21. Lock
Reentrant lock
Synchronized version
package juc.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"); call(); //There are locks here, too } public synchronized void call(){ System.out.println(Thread.currentThread().getName() + "call"); } }
Lock version
package juc.lock; 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 synchronized 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"); call();//There are locks here, too } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public synchronized void call(){ lock.lock(); try { System.out.println(Thread.currentThread().getName() + "call"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
Spin lock
package juc.lock; import java.util.concurrent.atomic.AtomicReference; /** * Spin lock */ public class SpinLockDemo { AtomicReference<Thread> atomicReference = new AtomicReference<>(); // Lock public void myLock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "==> mylock"); // Spin lock while(!atomicReference.compareAndSet(null, thread)){ } } // Unlock // Lock public void myUnLock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "==> myUnlock"); atomicReference.compareAndSet(thread, null); } }
package juc.lock; import java.util.concurrent.TimeUnit; public class TextSpinLock { public static void main(String[] args) throws InterruptedException { SpinLockDemo spinLockDemo = new SpinLockDemo(); new Thread(() -> { spinLockDemo.myLock(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { e.printStackTrace(); } finally { spinLockDemo.myUnLock(); } }, "A").start(); TimeUnit.SECONDS.sleep(1); new Thread(() -> { spinLockDemo.myLock(); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } finally { spinLockDemo.myUnLock(); } }, "B").start(); } }
deadlock
package juc.lock; import java.util.concurrent.TimeUnit; public class DeadLockDemo { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new MyThread(lockA, lockB), "A").start(); new Thread(new MyThread(lockB, lockA), "A").start(); } } class MyThread implements Runnable{ private String lockA; private String lockB; public MyThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA){ System.out.println(Thread.currentThread().getName() + "lock:" + lockA + "=>get" + lockB); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){ System.out.println(Thread.currentThread().getName() + "lock:" + lockB + "=>get" + lockA); } } } }