JUC concurrent programming
-
What is JUC
It refers to the abbreviation of these three packages in java
Small knowledge review
A process can have multiple threads, at least one
java has two threads by default
- main thread
- GC thread
java itself cannot start the thread. It calls the local native method
6 states of threads
public enum State { //function NEW, //function RUNNABLE, //block BLOCKED, //wait for WAITING, //Timeout wait TIMED_WAITING, //termination TERMINATED; }
-
View the number of native CPUs (threads)
package com.zyc.test; public class Test { public static void main(String[] args) { //The runtime queries the number of threads currently available, that is, the number of cpu cores System.out.println(Runtime.getRuntime().availableProcessors()); } }
-
The difference between wait and sleep
- From different classes
wait => Object
sleep => Thread
-
About lock release
wait will release the lock, sleep will not
-
Different scope of use
wait must be in the synchronization code block;
Sleep can sleep anywhere;
-
Lock lock
package com.zyc.test; public class Tickets { public static void main(String[] args) { ticket ticket = new ticket(); // new Thread(); //The interface can be new // new Thread(new Runnable() { // @Override // public void run() { // } // }); //Usage of lambad expression () - > {} new Thread(()->{ for (int i = 0; i < 40; i++) { ticket.sale(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 40; i++) { ticket.sale(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 40; i++) { ticket.sale(); } },"C").start(); } } class ticket{ private Integer t = 30; public void sale(){ if(t>0){ System.out.println(Thread.currentThread().getName()+" Sold the second"+t+" Ticket,surplus:"+t+" Ticket"); t--; } } }
The simplest is to add synchronized
Fair lock: very fair, must come first ~;
Unfair lock: very unfair, you can jump the queue; (non fair lock by default)
Code using lock lock
package com.zyc.test; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Ticketlock { public static void main(String[] args) { ticket2 ticket2 = new ticket2(); new Thread(()->{for(int i=0;i<40;i++) ticket2.sale(); },"A").start(); new Thread(()->{for(int i=0;i<40;i++) ticket2.sale(); },"B").start(); new Thread(()->{for(int i=0;i<40;i++) ticket2.sale(); },"C").start(); } } class ticket2{ private Integer t2 = 30; //Create a reentrant lock Lock lock = new ReentrantLock(); public void sale(){ lock.lock();//Lock try { if(t2>0){ System.out.println(Thread.currentThread().getName()+" Sold the second"+t2+" Ticket,surplus:"+t2+" Ticket"); t2--; } } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock();//Unlock } } }
I The difference between synchronized and lock locks
1. Synchronized built-in Java keyword, Lock is a Java class
2. Synchronized cannot judge the state of obtaining the Lock, but Lock can judge
3. Synchronized will automatically release the lock. Lock must be manually locked and manually released! Deadlock may be encountered
4. Synchronized thread 1 (get lock - > block), thread 2 (wait);
Lock will not necessarily wait all the time. Lock will have a trylock to try to obtain the lock, which will not cause a long wait.
5. Synchronized is a reentrant lock, non interruptible and unfair; Lock, which is reentrant, can judge the lock, and can set its own fair lock and unfair lock;
6. Synchronized is suitable for locking a small number of code synchronization problems, and Lock is suitable for locking a large number of synchronization codes;
II Producer consumer issues ******* (key)
- Implementation using synchronized wait notify
- Producer consumer is 1 Wait 2 Business 3 notice
public class A { public static void main(String[] args) { Data data = new Data(); 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(); } } class Data{ //Digital resources private int number = 0; //+1 public synchronized void increment() throws InterruptedException { if(number!=0){ //Wait for operation this.wait(); } number++; System.out.println(Thread.currentThread().getName()+"=>"+number); //Notify other threads that I + 1 is over this.notifyAll(); } //-1 public synchronized void decrement() throws InterruptedException { if(number==0){ //Wait for operation this.wait(); } number--; System.out.println(Thread.currentThread().getName()+"=>"+number); //Notify other threads that I -1'm finished this.notifyAll(); } }
But what if the number of threads now changes from two to four?
Originally, if judgment was used to wait for resources, and only one judgment will be made
Solution: using the while loop can prevent false wake-up problems
- Implementation of JUC method
- Replace wait with await and notify with signal
- The condition class is used
public class B { public static void main(String[] args) { Data2 data = new Data2(); 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(); new Thread(()->{for(int i=0;i<10;i++) { data.increment(); } },"C").start(); new Thread(()->{for(int i=0;i<10;i++) { data.decrement(); } },"D").start(); } } class Data2{ //Digital resources private int number = 0; //Lock lock Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //+1 public void increment() { lock.lock(); try{ //business while (number!=0){ //Wait for operation condition.await(); } number++; System.out.println(Thread.currentThread().getName()+"=>"+number); //Notify other threads that I + 1 is over condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } //-1 public void decrement() { lock.lock(); try{ //business while (number==0){ //Wait for operation condition.await(); } number--; System.out.println(Thread.currentThread().getName()+"=>"+number); //Notify other threads that I + 1 is over condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
Accurate notification and wake-up using condition
We wake up the next thread to be used with signal every time, so as to realize accurate notification
/** * A Call B after execution * B Call C after execution * C Call A after execution */ public class C { public static void main(String[] args) { Data3 data3 = new Data3(); new Thread(()->{ for(int i=0;i<10;i++){ data3.printA(); } },"A").start(); new Thread(()->{ for(int i=0;i<10;i++){ data3.printB(); } },"B").start(); new Thread(()->{ for(int i=0;i<10;i++){ data3.printC(); } },"C").start(); } } class Data3{ //Resource class private Lock lock=new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); private int number = 1; //1A 2B 3C public void printA(){ lock.lock(); try { //Business judgment - > execution - > notification while(number!=1){ //wait for condition1.await(); } //operation System.out.println(Thread.currentThread().getName()+",AAAAA"); //Wake up the specified thread number=2; condition2.signal(); // Wake up 2 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB(){ lock.lock(); try { //Business judgment - > execution - > notification while (number!=2){ condition2.await(); } System.out.println(Thread.currentThread().getName()+",BBBBB"); //Wake up 3 number=3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC(){ lock.lock(); try { //Business judgment - > execution - > notification while(number!=3){ condition3.await(); } System.out.println(Thread.currentThread().getName()+",CCCCC"); //Wake up 1 number=1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
III Eight lock problem
Eight lock example
package com.zyc.lock8; import java.util.concurrent.TimeUnit; //Lock 1: for objects locked with synchronize, the first person who obtains the lock in the execution process will execute it first, // Reason: it is not executed in sequence! Because the object of synchronized lock is a method call! For the two methods, the same lock is used. Who gets it first and who executes it first! The other is waiting! public class locktest1 { public static void main(String[] args) throws InterruptedException { phone phone = new phone(); new Thread(()->{ phone.sendmsg(); },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone.call(); }).start(); } } class phone{ public synchronized void sendmsg(){ System.out.println("send"); } public synchronized void call(){ System.out.println("call"); } }
package com.zyc.lock8; import java.util.concurrent.TimeUnit; //Lock 2: delay the first method by 4 seconds, and the result is still that the first method executes first. The reason is that the first method gets the lock first and locks it, while the other method waits public class locktest2 { public static void main(String[] args) throws InterruptedException { phone2 phone = new phone2(); new Thread(()->{ try { phone.sendmsg(); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone.call(); }).start(); } } class phone2{ public synchronized void sendmsg() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("send"); } public synchronized void call(){ System.out.println("call"); } }
package com.zyc.lock8; import java.util.concurrent.TimeUnit; //Lock 3: if a common method is added, the common method will be executed first, because the common method is not locked public class locktest3 { public static void main(String[] args) throws InterruptedException { phone3 phone = new phone3(); new Thread(()->{ try { phone.sendmsg(); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone.call(); }).start(); new Thread(()->{ phone.hello(); }).start(); } } class phone3{ public synchronized void sendmsg() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("send"); } public synchronized void call(){ System.out.println("call"); } public void hello (){ System.out.println("hello"); } }
package com.zyc.lock8; import java.util.concurrent.TimeUnit; //Lock 4: two objects are used to perform the phone operation of SMS respectively. At this time, the locking results are two objects, so the method of calling without delay is executed first public class locktest4 { public static void main(String[] args) throws InterruptedException { phone4 phone = new phone4(); phone4 phone2 = new phone4(); new Thread(()->{ try { phone.sendmsg(); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone2.call(); }).start(); } } class phone4{ public synchronized void sendmsg() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("send"); } public synchronized void call(){ System.out.println("call"); } public void hello (){ System.out.println("hello"); } }
package com.zyc.lock8; import java.util.concurrent.TimeUnit; //Locks 5 and 6: Here we use the static modification and the static synchronization method of locking. The lock is the template of this class, that is, no matter how many objects are created, one lock will be used at the same time public class locktest5 { public static void main(String[] args) throws InterruptedException { phone5 phone = new phone5(); phone5 phone2 = new phone5(); new Thread(()->{ try { phone.sendmsg(); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone2.call(); }).start(); } } class phone5{ public static synchronized void sendmsg() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("send"); } public static synchronized void call(){ System.out.println("call"); } public void hello (){ System.out.println("hello"); } }
package com.zyc.lock8; import java.util.concurrent.TimeUnit; //Lock 78: if we use a static method and an ordinary method, the ordinary method will execute directly, because there is no lock, // If there is a static synchronization method and a synchronization method, the two objects do not use the same lock, so who is faster will not be affected first public class locktest6 { public static void main(String[] args) throws InterruptedException { phone6 phone = new phone6(); new Thread(()->{ try { phone.sendmsg(); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone.hello(); }).start(); } } class phone6{ public static synchronized void sendmsg() throws InterruptedException { TimeUnit.SECONDS.sleep(4); System.out.println("send"); } public static synchronized void call(){ System.out.println("call"); } public void hello (){ System.out.println("hello"); } }
IV Collection class threading problem
list
package com.zyc.synchroCollection; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; public class synlist { //java. util. Concurrent modificationexception concurrent modification exception! The thread is not safe at this time public static void main(String[] args) { List<Object> arrayList = Collections.synchronizedList(new ArrayList<>()); List<String> list = new CopyOnWriteArrayList<>(); //sing another song /** * 1.Change to vertor class thread safety * 2.Using collections synchronizedList(new ArrayList<>()); * 3.Use the CopyOnWriteArrayList method in our juc * * */ for(int i=1;i<=10;i++){ new Thread(()->{ arrayList.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(arrayList); },String.valueOf(i)).start(); } } }
CopyOnWriteArrayList: copy when writing! An optimization strategy in the field of COW computer programming
When multiple threads call, list, read, fixed, write (overwrite operation exists); Avoid overwriting when writing, resulting in data disorder;
Comparison of vector and CopyOnWriteArrayList
- The bottom layer of vector is implemented with synchronized
- However, the bottom layer of CopyOnWriteArrayList is implemented with lock lock
CopyOnWriteArrayList is more efficient
set
package com.zyc.synchroCollection; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; public class synset { //Similarly: Java util. ConcurrentModificationException public static void main(String[] args) { // Set<String> hashSet = Collections.synchronizedSet(new HashSet<>()); Set<String> hashSet = new CopyOnWriteArraySet<>(); /** * 1.Collections.synchronizedSet(new HashSet<>()); * 2.new CopyOnWriteArraySet<>(); * hashset The bottom layer of is hashmap * * * */ for (int i = 1; i < 100; i++) { new Thread(()->{ hashSet.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(hashSet); },String.valueOf(i)).start(); } } }
The bottom layer of hashset is hashmap
Looking at the underlying source code, we find that the add method uses the key value of hashmap and cannot be repeated, so it becomes hashset
map
package com.zyc.synchroCollection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class synmap { public static void main(String[] args) { //Is map used like this? No, not at work //What is the default equivalence? new HashMap<>(16,0.75); Map<String, String> map = Collections.synchronizedMap(new HashMap<>()) ; Map map1 = new ConcurrentHashMap(); /** * 1.Using collections synchronizedMap(new HashMap<>()); handle; * 2.Concurrent processing using ConcurrentHashMap * * */ //Load factor, initialization capacity for (int i = 1; i < 100; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5)); System.out.println(map); },String.valueOf(i)).start(); } } }
callable
package com.zyc.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 { for (int i = 1; i < 10; i++) { // new Thread(new Runnable()).start(); // new Thread(new FutureTask<>( Callable)).start(); MyThread thread= new MyThread(); //Adaptation class: FutureTask FutureTask<String> futureTask = new FutureTask<>(thread); //Put it into Thread for use new Thread(futureTask,String.valueOf(i)).start(); //Get return value String s = futureTask.get(); System.out.println("Return value:"+ s); } } } class MyThread implements Callable<String> { @Override public String call() throws Exception { System.out.println("Call:"+Thread.currentThread().getName()); return "String"+Thread.currentThread().getName(); } }
V Common auxiliary classes
CountDownLatch
The subtraction counter continues to operate when the number decreases to 0
Use countDown for subtraction
Wait operation with await
//This is a counter subtraction public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { //The total is 6 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6 ; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+" Go out"); countDownLatch.countDown(); //Number of threads per thread - 1 },String.valueOf(i)).start(); } countDownLatch.await(); //Wait for the counter to zero and execute down System.out.println("close door"); } }
CyclickBarrier
Addition counter
public class CyclicBarrierDemo { public static void main(String[] args) { //Main thread CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("Summon the Dragon~"); }); for (int i = 1; i <= 7; i++) { //Child thread int finalI = i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+" Collected the third {"+ finalI+"} Dragon Ball"); try { cyclicBarrier.await(); //Add count wait } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } } }
Semaphore
Semaphore
semaphore.acquire() gets the resource. If the resource has been used up, wait for the resource to be released before using it!
semaphore.release() releases the current semaphore by + 1, and then wakes up the waiting thread!
Function: mutually exclusive use of multiple shared resources! Concurrent flow restriction, control the maximum number of threads!
public class SemaphoreDemo { public static void main(String[] args) { //There are 3 parking spaces Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 6; i++) { int finalI = i; new Thread(()->{ try { semaphore.acquire(); //obtain //Grab a parking space System.out.println(Thread.currentThread().getName()+" Grabbed the parking space{"+ finalI +"}"); TimeUnit.SECONDS.sleep(2); //Stop for 2s System.out.println(Thread.currentThread().getName()+" Leave the parking space"); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release();//release } },String.valueOf(i)).start(); } } }
Vi Read write lock
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; public class ReadWriteLockDemo { public static void main(String[] args) { MyCache_ReadWriteLock mycache = new MyCache_ReadWriteLock(); //Start 5 threads to write data for (int i = 1; i <=5 ; i++) { int finalI = i; new Thread(()->{ mycache.put(String.valueOf(finalI),String.valueOf(finalI)); }).start(); } //Open 10 threads to read data for (int i = 1; i <=10 ; i++) { int finalI = i; new Thread(()->{ String o = mycache.get(String.valueOf(finalI)); }).start(); } } } class MyCache_ReadWriteLock{ private volatile Map<String,String> map=new HashMap<>(); //Use read-write lock private ReadWriteLock readWriteLock=new ReentrantReadWriteLock(); //Ordinary lock private Lock lock=new ReentrantLock(); public void put(String key,String value){ //Lock readWriteLock.writeLock().lock(); try { //write in //operation flow System.out.println(Thread.currentThread().getName()+" Thread starts writing"); map.put(key, value); System.out.println(Thread.currentThread().getName()+" Thread write OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.writeLock().unlock(); //Unlock } } public String get(String key){ //Lock String o=""; readWriteLock.readLock().lock(); try { //obtain System.out.println(Thread.currentThread().getName()+" Thread starts reading"); o = map.get(key); System.out.println(Thread.currentThread().getName()+" Thread read OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } return o; } }
VII Blocking queue
Use blocking queues in the case of multithreading, concurrent processing, or thread pools
The whole blocking Queue family is as follows: Deque, AbstaractQueue and BlockingQueue are implemented below Queue;
Below BlockingQueue, there are blocking queues implemented by Link linked lists and Array arrays
Four API s for blocking queues
mode | Throw exception | No exception will be thrown and there is a return value | Blocking waiting | Timeout wait |
---|---|---|---|---|
add to | add | offer | put | offer(timenum,timeUnit) |
remove | remove | poll | take | poll(timenum,timeUnit) |
Judge the queue head | element | peek | - | - |
- Using the add remove operation
- This means that an exception will be thrown when the queue is out of range
public static void test1(){ //The size of the queue that needs to be initialized ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); System.out.println(blockingQueue.add("a")); System.out.println(blockingQueue.add("b")); System.out.println(blockingQueue.add("c")); //Throw exception: Java lang.IllegalStateException: Queue full // System.out.println(blockingQueue.add("d")); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); //If you remove one more //This also causes Java util. NoSuchElementException throws an exception System.out.println(blockingQueue.remove()); }
- Use offer pull
- This is to return false and null values when the queue exceeds the limit without throwing an exception
/** * No exception is thrown and there is a return value */ public static void test2(){ ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); //Adding an element that cannot be added, using offer will only return false and will not throw an exception System.out.println(blockingQueue.offer("d")); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); //If there are no pop-up elements, it will only return null and will not throw an exception System.out.println(blockingQueue.poll()); }
- Use put take
- This is to wait when the queue exceeds or there are no elements, resulting in the program waiting all the time
/** * Waiting has been blocked */ public static void test3() throws InterruptedException { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); //Always blocked and will not return blockingQueue.put("a"); blockingQueue.put("b"); blockingQueue.put("c"); //If the queue is full, enter another element. In this case, you will wait until when the queue has a position to enter again, and the program will not stop // blockingQueue.put("d"); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); //If we do it again, this situation will also wait, and the program will keep running and blocking System.out.println(blockingQueue.take()); }
- Use the method of offer pull with parameters
- You can set the corresponding timeout. If the timeout occurs, the corresponding operation will be abandoned
/** * Timeout blocking * In this case, it will also wait for the queue to have a position or products, but it will end over time */ public static void test4() throws InterruptedException { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); blockingQueue.offer("a"); blockingQueue.offer("b"); blockingQueue.offer("c"); System.out.println("Start waiting"); blockingQueue.offer("d",2, TimeUnit.SECONDS); //The timeout is 2s. If it exceeds 2s, the waiting will end System.out.println("End waiting"); System.out.println("===========Value=================="); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println("Start waiting"); blockingQueue.poll(2,TimeUnit.SECONDS); //We won't wait for more than two seconds System.out.println("End waiting"); }
SynchronousQueue synchronization queue
If the synchronization queue has no capacity, it can also be regarded as a queue with capacity of 1;
When you enter an element, you must wait for it to be taken out before you can put another element into it;
Use put method and take method;
Unlike other blockingqueues, synchronous queue does not store elements;
If you put an element, you must take it out first, otherwise you can't put in the value!
And the take of SynchronousQueue uses lock lock to ensure thread safety.
/** * Synchronization queue */ public class SynchronousQueueDemo { public static void main(String[] args) { BlockingQueue<String> synchronousQueue = new SynchronousQueue<>(); //Study if it is judged that this is a synchronous queue //Use two processes // Put a process in // Take out a process new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+" Put 1"); synchronousQueue.put("1"); System.out.println(Thread.currentThread().getName()+" Put 2"); synchronousQueue.put("2"); System.out.println(Thread.currentThread().getName()+" Put 3"); synchronousQueue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } },"T1").start(); new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+" Take "+synchronousQueue.take()); // TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+" Take "+synchronousQueue.take()); // TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+" Take "+synchronousQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } },"T2").start(); } }
VIII Thread pool
Three methods, seven parameters and four rejection strategies
Because the program uses resources, it will occupy resources. In order to optimize the use of resources, pooling technology appears
Because the creation and destruction of resources will consume memory
Pooling Technology: prepare some resources in advance. If someone wants to use them, come to me and return them to me after use, so as to improve efficiency.
Benefits of thread pool:
1. Reduce resource consumption;
2. Improve the speed of response;
3. Convenient management;
Thread reuse can control the maximum concurrent number and manage threads
Three methods
Three methods
- ExecutorService threadPool = Executors.newSingleThreadExecutor();// Single thread
- ExecutorService threadPool2 = Executors.newFixedThreadPool(5); // Create a fixed thread pool size
- ExecutorService threadPool3 = Executors.newCachedThreadPool(); // Scalable, cache thread pool
//Three methods of tool Executors; public class Demo01 { public static void main(String[] args) { ExecutorService threadPool = Executors.newSingleThreadExecutor();//Single thread ExecutorService threadPool2 = Executors.newFixedThreadPool(5); //Create a fixed thread pool size ExecutorService threadPool3 = Executors.newCachedThreadPool(); //Scalable //When the thread pool runs out, you must close the thread pool try { for (int i = 1; i <=100 ; i++) { //Create threads from thread pool threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+ " ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
7 parameters
Firstly, it is analyzed that the underlying implementations of the three methods are created using ThreadPoolExecutor
Creating a thread pool using excellers will cause resource consumption. It is also written in Alibaba's development manual to force us to directly use ThreadPoolExecutor to create a thread pool to save resource consumption
- ThreadPoolExecutor
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 factories generally do not need to move when creating threads RejectedExecutionHandler handler //Reject strategy ) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
Realization of 7 parameters
- If the current number of threads is less than the number of corePoolSize core threads, a new thread will be created each time a task needs a thread
- If the number of threads is greater than the corePoolSize but not the maximum number of maximumPoolSize, it will first try to add the task to the task queue. After the addition is successful, the task will wait. After the resource is released, the thread is operating. At this time, the maximum task capacity is the number of core threads + the number of blocked queue tasks
- If the number of task queues is greater than maximumPoolSize, a new thread will be created for task processing. At this time, the maximum capacity is the maximum number of threads + the number of blocked queues
- If the number of threads is greater than maximumPoolSize, reject metering will be performed
Personal understanding: we can understand that when a bank handles business, the thread is the counter, the number of our core threads is the counter that has been open all the time, and the blocking queue is the position of the rest area. Before reaching the number of core threads, each person who handles business will open a counter, and when there are many people, we will go to the rest area to rest. If there are more people, we will open the counter, Until the maximum number of counters is reached, if another person handles business, we will use the rejection strategy to drive away. Overtime waiting means that if there is no business within the specified time after the non core counter is opened, we will let him off duty.
Four rejection strategies
-
new ThreadPoolExecutor.AbortPolicy() / /: the rejection policy is: if the bank is full and someone else comes in, don't deal with this person's and throw an exception
If the maximum load is exceeded, an exception will be thrown: queue capacity + maxPoolSize
-
new ThreadPoolExecutor.CallerRunsPolicy() / /: the rejection policy is: where comes from, where goes. The main thread handles it
-
new ThreadPoolExecutor.DiscardPolicy(): / / the rejection policy is: if the queue is full, throw out the exception and no exception will be thrown.
-
new ThreadPoolExecutor.DiscardOldestPolicy() / /: the rejection policy is: if the queue is full, try to compete with the earliest process without throwing exceptions
CPU intensive and IO intensive
That is, where is optimization
CPU intensive: select the number of cores of the computer; Select the size of maximunPoolSize
We can use the code system out. println(Runtime.getRuntime(). availableProcessors());
Get cpc quantity
I/O intensive:
There are 15 large tasks in the program, which io takes up a lot of resources; I/O intensive is to judge the number of I/O consuming threads in our program, which is about twice to twice the maximum number of I/O.
9, Four functional interfaces
New requirements: lambda expression, chain programming, functional interface, Stream flow calculation
Functional interface: an interface with only one method inside
For classes that meet functional interfaces, we can use lambda expressions to simplify development
View jdk1 8 official documents under the juf package, we can see four functional interfaces
Consumer consumer interface
There is no return value, only the entered value
function interface
Parameter 1 is the incoming value and parameter 2 is the return value
Predicate predicate interface
The return value is Boolean and can be passed as a parameter
Super supply interface
No parameters, only return values
10, steam flow calculation
public class Test { public static void main(String[] args) { User user1 = new User(1,"a",21); User user2 = new User(2,"b",22); User user3 = new User(3,"c",23); User user4 = new User(4,"d",24); User user5 = new User(5,"e",25); User user6 = new User(6,"f",26); List<User> list = Arrays.asList(user1, user2, user3, user4, user5, user6); //Calculate the delivery flow //Chain programming!!!! 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); } }
How to open flow: object steam()
To meet the chain programming, the use of steam flow operation can improve the efficiency. It is recommended to use flow calculation
11, ForkJoin
ForkJoin is in jdk1 7. Execute tasks in parallel! Improve efficiency ~. In large data volume, the speed will be faster!
In big data: MapReduce core idea - > split big tasks into small tasks!
The task is divided into subtasks, which process the results respectively, and finally get the final result
ForkJoin features: job theft
When two threads execute in parallel, if one thread finishes executing first, this thread will come and grab the task of the other thread
How to use ForkJoin?
1. Execute through forkpool
2. Compute task execute (forkjointask <? > task)
3. The calculation class should inherit ForkJoinTask
Calculation class of ForkJoin!
package com.haust.forkjoin; import java.util.concurrent.RecursiveTask; /** * The task of summation calculation! * 3000 6000(ForkJoin) 9000(Stream Parallel stream) * // How to use forkjoin * // 1,forkjoinPool Execute through it * // 2,Calculate the task forkjoinpool execute(ForkJoinTask task) * // 3. The calculation class should inherit recursivetask (recursive task with return value) */ 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; } return sum; }else { // forkjoin recursion long middle = (start + end) / 2; // Intermediate value ForkJoinDemo task1 = new ForkJoinDemo(start, middle); task1.fork(); // Split the task and push the task into the thread queue ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end); task2.fork(); // Split the task and push the task into the thread queue return task1.join() + task2.join(); } } }
Test class
package com.ogj.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 { test1(); test2(); test3(); } /** * General calculation */ public static void test1(){ long star = System.currentTimeMillis(); long sum = 0L; for (long i = 1; i < 20_0000_0000; i++) { sum+=i; } long end = System.currentTimeMillis(); System.out.println("sum="+"Time:"+(end-star)); System.out.println(sum); } /** * Using ForkJoin */ public static void test2() throws ExecutionException, InterruptedException { long star = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinDemo(0L, 20_0000_0000L); ForkJoinTask<Long> submit = forkJoinPool.submit(task); Long aLong = submit.get(); System.out.println(aLong); long end = System.currentTimeMillis(); System.out.println("sum="+"Time:"+(end-star)); } /** * Use Stream parallel Stream */ public static void test3(){ long star = System.currentTimeMillis(); //Stream parallel stream () long sum = LongStream.range(0L, 20_0000_0000L).parallel().reduce(0, Long::sum); System.out.println(sum); long end = System.currentTimeMillis(); System.out.println("sum="+"Time:"+(end-star)); } }
12, Asynchronous callback
package com.haust.future; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; /** * 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 // The error message is returned; CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{ System.out.println(Thread.currentThread().getName() +"supplyAsync=>Integer"); int i = 10/0; return 1024; }); System.out.println(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 }).get()); /** * succee Code 200 * error Code 404 500 */ } }
13, JMM (java memory model)
Volatile: volatile is a lightweight synchronization mechanism provided by Java virtual machine, which is similar to synchronized but not as powerful.
1. Ensure visibility
2. Atomicity is not guaranteed
3. Prevent instruction rearrangement
Some synchronization conventions of JMM:
1. Before the thread is unlocked, the shared variable must be flushed back to main memory immediately.
2. Before locking the thread, you must read the latest value in the main memory into the working memory!
3. Locking and unlocking are the same lock.
There are eight operations for memory interaction:
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 writ e operations on some platforms)
lock (Lock): a variable that acts on the main memory and identifies a variable as a thread exclusive state unlock (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 (Read): acts on the main memory variable, which transfers the value of a variable from the main memory to the working memory of the thread for subsequent processing load Action use load (Load): a variable that acts on working memory, which puts read The operation puts variables from main memory into working memory use (Use): it acts on the variable in working memory, which transfers the variable 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 (Assignment): it acts on a variable in working memory. It puts a value received from the execution engine into the variable copy in working memory store (Storage): a variable that acts on the main memory. It transfers the value of a variable from the working memory to the main memory for subsequent processing write use write (Write): a variable that acts on the main memory and puts store The value of the variable obtained from the working memory is put into the variable in the main memory
JMM formulates the following rules for the use of these eight instructions:
not allow read and load,store and write One of the actions appears separately. That is, it is used read must load,Used store must write The thread is not allowed to discard its most recent assign Operation, that is, after the data of the working variable is changed, the main memory must be informed A thread is not allowed. There will be no assign Data is synchronized 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. Variable implementation use,store Before operation, you must go through assign and load operation Only one thread can control a variable at a time lock. many times lock After, you must perform the same number of operations unlock To unlock If you change a variable lock The operation will clear the value of this variable in all working memory. Before the execution engine uses this variable, it must be reset load or assign The value of the operation initialization variable If a variable is not lock,You can't do it unlock Operation. Neither unlock A variable locked by another thread To a variable unlock This variable must be synchronized back to main memory before operation
14, volatile
1. Ensure visibility
package com.haust.tvolatile; import java.util.concurrent.TimeUnit; public class JMMDemo { // If you don't add volatile, the program will loop! // Adding volatile ensures visibility 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){ } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } num = 1; System.out.println(num); } }
2. Atomicity is not guaranteed
package com.haust.tvolatile; import java.util.concurrent.atomic.AtomicInteger; // volatile does not guarantee atomicity public class VDemo02 { // volatile does not guarantee atomicity // Integer of atomic class private volatile static AtomicInteger num = new AtomicInteger(); public static void add(){ // num++; // Not an atomic operation num.getAndIncrement(); // AtomicInteger + 1 method, CAS } public static void main(String[] args) { //Theoretically, the num result should be 20000 for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 0; j < 1000 ; j++) { add(); } }).start(); } // Judge that as long as there are no more than 2 remaining threads, it means that the execution of 20 created threads has ended while (Thread.activeCount()>2){ // Java has two main GC threads by default Thread.yield(); } System.out.println(Thread.currentThread().getName() + " " + num); } }
Solution: use the atomic class under juc to solve atomicity, and there is no need to use synchronized and lock
// volatile does not guarantee atomicity // Integer of atomic class private volatile static AtomicInteger num = new AtomicInteger(); public static void add(){ // num++; // Not an atomic operation num.getAndIncrement(); // AtomicInteger + 1 method, CAS }
3. Avoid instruction rearrangement
When the computer compiles code, the order of instructions may be reordered, but it is not random because of association constraints
We use volatile to avoid instruction rearrangement: there is a memory barrier
- Ensure the execution sequence of specific operations!
- Memory visibility of some variables can be guaranteed (visibility is achieved by using these features volatile)
15, Singleton mode
Hungry Han single instance mode:
package com.zyc.playSingle; //Hungry Han single case mode public class hungryMan { //Private constructor, so you can't create an instance through the constructor private hungryMan(){ } private static final hungryMan HUNGR_MAN = new hungryMan(); //Give it as long as you have it: for the characteristics of hungry men, use static, so that you can access it directly with classes public static hungryMan getHungrMan(){ return HUNGR_MAN; } } class t1{ public static void main(String[] args) { hungryMan hungrMan = com.zyc.playSingle.hungryMan.getHungrMan(); hungryMan hungrMan1 = hungryMan.getHungrMan(); hungryMan hungrMan2 = hungryMan.getHungrMan(); System.out.println(hungrMan); System.out.println(hungrMan1); System.out.println(hungrMan2); } }
Lazy singleton mode:
package com.zyc.playSingle; //Lazy singleton mode public class lazyMan { private lazyMan(){ } private static lazyMan LAZY_MAN ; public static lazyMan getLAZY_MAN(){ if (LAZY_MAN==null){ LAZY_MAN= new lazyMan(); return LAZY_MAN; } return LAZY_MAN; } } class t2{ public static void main(String[] args) { lazyMan lazy_man = lazyMan.getLAZY_MAN(); lazyMan lazy_man1 = lazyMan.getLAZY_MAN(); System.out.println(lazy_man); System.out.println(lazy_man1); } }
In the case of single thread, it is safe to use singleton mode. What about multithreading?
package com.zyc.playSingle; public class DCLLazyMan { private DCLLazyMan(){ System.out.println(Thread.currentThread().getName()+"ing...."); //Test the thread name of creating a singleton for each print } private static DCLLazyMan dclLazyMan; //Lazy mode public static DCLLazyMan getDclLazyMan(){ if(dclLazyMan==null){ dclLazyMan = new DCLLazyMan(); } return dclLazyMan; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ DCLLazyMan.getDclLazyMan(); }).start(); } } }
Here we use 10 threads to create a singleton. The results are as follows:
We see different results, so in the case of multithreading, this thread is not safe
Solution: lock
//Dual detection lock mode DCL lazy public static DCLLazyMan getDclLazyMan(){ if (dclLazyMan==null){ synchronized (DCLLazyMan.class){ if(dclLazyMan==null){ dclLazyMan = new DCLLazyMan(); } } } return dclLazyMan; }
Using the double detection lock, we see the following results:
As a result, our requirements are now met. Is the threading problem fully guaranteed?
Instruction rearrangement
Reason: create object dclLazyMan = new DCLLazyMan(); This line of code is not an atomic operation, which may cause instruction rearrangement (1. Allocate memory space 2. Initialize objects 3. Point to memory space. / / the order of these three steps will be reordered. It is possible to perform operation 3 first and then operation 2)
So we need to use volatile to avoid instruction rearrangement
The changed code is as follows:
package com.zyc.playSingle; public class DCLLazyMan { private DCLLazyMan(){ System.out.println(Thread.currentThread().getName()+"ing...."); } private volatile static DCLLazyMan dclLazyMan; //Dual detection lock mode DCL lazy public static DCLLazyMan getDclLazyMan(){ if (dclLazyMan==null){ synchronized (DCLLazyMan.class){ if(dclLazyMan==null){ dclLazyMan = new DCLLazyMan(); } } } return dclLazyMan; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ DCLLazyMan.getDclLazyMan(); }).start(); } } }
Using reflection to crack singleton mode
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { DCLLazyMan dclLazyMan1 = DCLLazyMan.getDclLazyMan(); Constructor<DCLLazyMan> declaredConstructor = DCLLazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); DCLLazyMan dclLazyMan = declaredConstructor.newInstance(); System.out.println(dclLazyMan); System.out.println(dclLazyMan1); }
After using reflection, you can find that creating two objects is different;
The solution is judged by the constructor
private static boolean flag = false; private DCLLazyMan(){ synchronized (DCLLazyMan.class){ if (flag==false){ flag=true; } else{ throw new RuntimeException("Do not attempt to use reflection to break exceptions"); } } System.out.println(Thread.currentThread().getName()+"ing...."); }
But we created an object with reflection and compared it with the original object. What if we created objects with all reflection?
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor<DCLLazyMan> declaredConstructor = DCLLazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); DCLLazyMan dclLazyMan = declaredConstructor.newInstance(); DCLLazyMan dclLazyMan1 = declaredConstructor.newInstance(); System.out.println(dclLazyMan); System.out.println(dclLazyMan1); }
Solution: use an identifier
private static boolean flag = false; private DCLLazyMan(){ synchronized (DCLLazyMan.class){ if (flag==false){ flag=true; } else{ throw new RuntimeException("Do not attempt to use reflection to break exceptions"); } } System.out.println(Thread.currentThread().getName()+"ing...."); }
But what if we get the identifier we set through decompilation or other means?
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { Field flag = DCLLazyMan.class.getDeclaredField("flag"); flag.setAccessible(true); Constructor<DCLLazyMan> declaredConstructor = DCLLazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); DCLLazyMan dclLazyMan = declaredConstructor.newInstance(); flag.set(dclLazyMan,false); DCLLazyMan dclLazyMan1 = declaredConstructor.newInstance(); System.out.println(dclLazyMan); System.out.println(dclLazyMan1); }
By modifying the identifier, our singleton pattern is broken again
Solution: use enumeration through source code analysis
Let's use enumeration types for analysis
Create enumeration classes and then create objects with reflection:
package com.zyc.playSingle; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; //What is enum? Enum itself is a Class class public enum enumSingle { INSTANCE; public enumSingle getInstance(){ return INSTANCE; } } class Test{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { enumSingle instance1 = enumSingle.INSTANCE; Constructor<enumSingle> declaredConstructor = enumSingle.class.getDeclaredConstructor(); declaredConstructor.setAccessible(true); enumSingle instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }
Errors are reported as follows:
We found that the results are different from those read in the source code:
After decompiled by JAD tool, we can get that here is not an empty parameter constructor, but a parametric constructor
Constructor<enumSingle> declaredConstructor = enumSingle.class.getDeclaredConstructor(String.class,int.class);
After modification:
The exception thrown is correct, and the enumeration class is successfully used to avoid the destruction of reflection
16, CAS
What is cas: java level compareAndSet (expected value, updated value)
public class casDemo { //CAS: compareandset compare and exchange public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2020); //boolean compareAndSet(int expect, int update) //Expected value, updated value //If the actual value is the same as my expectation, update it //If the actual value is different from my expectation, it will not be updated System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); //Because the expected value is 2020, but the actual value becomes 2021, the modification will fail //CAS is the concurrency primitive of CPU atomicInteger.getAndIncrement(); //++Operation System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); } }
Spin lock is used at the bottom of source code analysis
Spin lock: keep cycling until conditions are met to unlock
CAS operation calls memory for storage, which is very efficient, but it is not directly manipulated by Java
Instead, it calls the unsafe class, which calls the underlying c++
Summary:
CAS: compare the value in the current working memory with the value in the main memory. If this value is expected, then execute the operation! If you don't keep cycling, you use a spin lock.
Disadvantages:
- The cycle will take time;
- One time can only guarantee the atomicity of one shared variable;
- It will have ABA problems
ABA problem
That is, when one thread changes the current memory information and changes the current information back after the operation, but the other thread still reads the original value, and there is no way to judge whether the operation has occurred
Solution: use optimistic locks
Set a timestamp and perform + + operations on the current timestamp each time
17, Lock
1. Fair lock and unfair lock
Fair lock: very fair; Those who cannot jump the queue must come first and arrive later;
public ReentrantLock() { sync = new NonfairSync(); }
Unfair lock: very unfair. If you are allowed to jump the queue, you can change the order.
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
2. Reentrant lock is also called recursive lock
Get the outside lock and the inside lock automatically
Two implementation methods
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 is also a lock here } public synchronized void call(){ System.out.println(Thread.currentThread().getName()+"=> call"); } }
//lock 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: This is two locks and two keys //The lock lock must be paired, otherwise it will deadlock in it try { System.out.println(Thread.currentThread().getName()+"=> sms"); call();//There is also a lock here } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void call(){ lock.lock(); try { System.out.println(Thread.currentThread().getName() + "=> call"); }catch (Exception e){ e.printStackTrace(); } finally { lock.unlock(); } } }
- lock locks must be paired, which means that the number of locks and unlock s must be the same;
- The lock added outside can also be unlocked inside; The lock added inside can also be unlocked outside;
3. Spin lock
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
Implementation of spin lock
public class SpinlockDemo { //int 0 //thread 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)){ System.out.println(Thread.currentThread().getName()+" ==> In spin~"); } } //Unlock public void myunlock(){ Thread thread=Thread.currentThread(); System.out.println(thread.getName()+"===> myUnlock"); atomicReference.compareAndSet(thread,null); } }
Test class
public class TestSpinLock { public static void main(String[] args) throws InterruptedException { ReentrantLock reentrantLock = new ReentrantLock(); reentrantLock.lock(); reentrantLock.unlock(); //Using CAS to realize spin lock SpinlockDemo spinlockDemo=new SpinlockDemo(); new Thread(()->{ spinlockDemo.myLock(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { e.printStackTrace(); } finally { spinlockDemo.myunlock(); } },"t1").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ spinlockDemo.myLock(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { e.printStackTrace(); } finally { spinlockDemo.myunlock(); } },"t2").start(); } }
4. Deadlock
package com.ogj.lock; import java.util.concurrent.TimeUnit; public class DeadLock { public static void main(String[] args) { String lockA= "lockA"; String lockB= "lockB"; new Thread(new MyThread(lockA,lockB),"t1").start(); new Thread(new MyThread(lockB,lockA),"t2").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); } } } }
How to solve the deadlock problem: use jps in the terminal of idea to debug ps -ef similar to linux
1. Use jps to locate the process number. There is a jps in the bin directory of jdk
Command: jps -l
2. Use jstack process number to find deadlock information
lic void myunlock(){
Thread thread=Thread.currentThread();
System.out.println(thread.getName()+"===> myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
Test class ```java public class TestSpinLock { public static void main(String[] args) throws InterruptedException { ReentrantLock reentrantLock = new ReentrantLock(); reentrantLock.lock(); reentrantLock.unlock(); //Using CAS to realize spin lock SpinlockDemo spinlockDemo=new SpinlockDemo(); new Thread(()->{ spinlockDemo.myLock(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { e.printStackTrace(); } finally { spinlockDemo.myunlock(); } },"t1").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ spinlockDemo.myLock(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { e.printStackTrace(); } finally { spinlockDemo.myunlock(); } },"t2").start(); } }
4. Deadlock
package com.ogj.lock; import java.util.concurrent.TimeUnit; public class DeadLock { public static void main(String[] args) { String lockA= "lockA"; String lockB= "lockB"; new Thread(new MyThread(lockA,lockB),"t1").start(); new Thread(new MyThread(lockB,lockA),"t2").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); } } } }
How to solve the deadlock problem: use jps in the terminal of idea to debug ps -ef similar to linux
1. Use jps to locate the process number. There is a jps in the bin directory of jdk
Command: jps -l
2. Use jstack process number to find deadlock information