Multithreading basics

What is a JUC?

Util is a toolkit in java.util

Can Java start threads?

No. It’s actually the local method that starts the thread.

What is the difference between concurrency and parallelism?

  1. Parallel: Multi-core cpus perform multiple tasks simultaneously
  2. Concurrency: Multiple threads performing multiple tasks simultaneously

How do I get the number of CPU cores?

Runtime.getRuntime().availableProcessors();

Why concurrent programming?

CPU/ memory /IO device speeds vary greatly, and operating systems add processes/threads to take advantage of CPU performance.

What are the problems that arise in order to make good use of CPU performance?

  1. In order to balance the speed difference between CPU and memory, the CPU increases the cache, which brings: visibility problems.
  2. To balance the speed difference between the CPU and the I/O device, the operating system added processes/threads and TDM cpus. It brings: atomic problems.
  3. In order to make better use of the cache, the compiler also spends the instruction order, which brings about the problem of ordering.

How many states does a thread have?

  1. NEW: NEW
  2. Run a RUNNABLE:
  3. BLOCKED: blocking
  4. WAITING, WAITING for
  5. TIMED_WAITING: timeout waiting
  6. TERMINATED TERMINATED:

What’s the difference between ‘wait’ and ‘sleep’?

  1. Class: Wait is Object, and sleep is Thread
  2. Release lock: Wait releases lock,sleep does not release lock
  3. Scope of use: Wait must be in the synchronized code block,sleep can be anywhere
  4. Catch exceptions or not: Wait does not need to catch exceptions, and sleep must

How do you guarantee… synchronize? ?

The following code executes in order after the synchronize lock is added.

public class SaleTicketDemo01 {
    public static void main(String[] args) {
         Ticket ticket = new Ticket();
         // All three threads will sell tickets to make sure they are sold out
        new Thread(() -> {
            for (int i = 0; i < 60; i++) { ticket.sale(); }},"A").start();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) { ticket.sale(); }},"B").start();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) { ticket.sale(); }},"c").start(); }}class Ticket {
    private int number = 50;
    public synchronized void sale(a) {// Sell tickets synchronously lock
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "There is still something left." + (number--) + "Ticket"); }}}Copy the code

Result: Tickets are sold in strict order.

A, 50 tickets A, 49 tickets A, 48 tickets A, 47 tickets A, 46 tickets......Copy the code

If you do not synchronize it is out of order.

Why is the above example out of order without locking?

Because the print statement above is not atomic. So it is possible that number– was executed, but the print was not immediately executed, and was preempted by another thread. If you carefully observe the result of out-of-order, you will find that it is generally in order, and the out-of-order occurs when the thread is switched.

What are fair locks and unfair locks?

Fair lock: first come first served. Unfair lock: out-of-order code:

public class MyFairLock {
    private  ReentrantLock lock = new ReentrantLock(true);//true indicates a fair ReentrantLock
    public   void testFail(a){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() +"Got the lock.");
        }finally{ lock.unlock(); }}public static void main(String[] args) {
        MyFairLock fairLock = new MyFairLock();
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName()+"Start");// Indicates the sequence
            fairLock.testFail();/ / into the lock
        };
        for(int i=0; i<100; i++){newThread(runnable).start(); }}}Copy the code

Results:

Thread-0 enabled thread-0 obtained the lock thread-3 enabled thread-2 Enabled thread-1 enabled thread-6 enabled thread-4 Enabled thread-3 obtained the lock thread-5 enabled thread-7 enabled Thread-13 enabled Thread-2 to obtain the lock......Copy the code

If the above example is changed to an unfair lock, the result is that the order in which the lock is started and acquired is out of order

What’s the difference between lock and sychronized?

  1. Type:synchronizeLock is a Java keyword. Lock is a Java class
  2. To determine whether a lock has been acquired:?????
  3. Automatic lock release: Synchronized automatically releases the lock, but lock does not. You need to manually release the lock

What is a lock?

Who holds the lock?

How do you know who’s locked?

How to write a producer-consumer pattern to use resources?

code

public class Test01 {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for(int i=0; i<10; i++){try {
                    data.increment();/ / producer
                } catch(InterruptedException e) { e.printStackTrace(); }}},"A").start();
        new Thread(()->{
            for(int i=0; i<10; i++){try {
                    data.decrement();/ / consumer
                } catch(InterruptedException e) { e.printStackTrace(); }}},"B").start(); }}// Determine whether to wait, execute the service, notify
class Data{// Digital resource class
    private int number = 0;/ / resources
    / / + 1
    public synchronized void increment(a) throws InterruptedException {
        while(number ! =0) {// With while, the condition is judged after the false wake up
            this.wait();// If not, wait
        }
        number++;// Execute specific business
        System.out.println(Thread.currentThread().getName()+"> > > > > > > > >"+number);
        this.notifyAll();// Notify other threads that I +1 is done
    }
    / / 1
    public synchronized void decrement(a) throws InterruptedException {
        while(number == 0) {this.wait();// Wait if you are finished
        }
        number--;// Perform business
        System.out.println(Thread.currentThread().getName()+"> > > > > > > > >"+number);
        // Notify other threads that I am -1 complete
        this.notifyAll(); }}Copy the code

Execution Result:

A>>>>>>>>>1
B>>>>>>>>>0
A>>>>>>>>>1
B>>>>>>>>>0
......
Copy the code

Producer consumer template: Wait/perform business/wake up. Even if there are more than two threads, the result is similar, with the producer producing and waiting for the consumer to consume. Note: Do not use if when waiting, otherwise false wake up may occur.

What is false awakening?

On multiprocessor systems, a program that issues a WAIT may wake up and continue execution without notify. In order not to slow down the efficiency of the conditional variable operation, the low-level wait function was not designed to ensure that every wake up was triggered by notify, and the task was left to the upper application to realize. It was necessary to define a loop to determine whether the condition could really meet the requirements of the program to continue running. Therefore, the judgment should be made by while instead of if, so that the judgment condition will be carried out every time to prevent false wake-up of wait.

How to solve false awakening?

Change one judgment to multiple judgments.

How to write producer-consumer usage resources if lock is used?

The logic of the test code remains the same. The difference is that the wait and wake logic has changed. Instead of using this.wait() and this.notifyall (), use condition.await() and condition.signalall () producer and consumer code as follows:

//lock.newCondition().await(); / / wait for
//ock.newCondition().signalAll(); / / wake
class Data2 {// Digital resource class
    private int number = 0;
    Lock lock = new ReentrantLock();// Create a new lock
    Condition condition = lock.newCondition();// Create a new condition
    / / + 1
    public void increment(a) throws InterruptedException {
        lock.lock();
        try {
            while(number ! =0) {
                / / wait for
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "> > > > > > > > >" + number);
            // Notify other threads that I +1 is done
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}/ / 1
    public void decrement(a) throws InterruptedException {
        lock.lock();
        try {
            while (number == 0) {
                / / wait for
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "> > > > > > > > >" + number);
            // Notify other threads that I am -1 complete
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}}Copy the code

Execution Result:

A>>>>>>>>>1
B>>>>>>>>>0
A>>>>>>>>>1
D>>>>>>>>>0
C>>>>>>>>>1
B>>>>>>>>>0
......
Copy the code

How to write a multiple producer-multiple consumer pattern to use resources?

Condition using ReentrantLock.ReentrantLock can define multiple conditions and can specify which conditions thread waits for and which threads wake up. Omit the test code as follows:

class Data3{/ / resource class
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();1 / / conditions
    private Condition condition2 = lock.newCondition();2 / / conditions
    private Condition condition3 = lock.newCondition();3 / / conditions
    private int number = 1;//1A 2B 3C
    public void printA(a){
        lock.lock();
        try {
            // Decide to wait -> execute -> wake up
            while(number ! =1) {// Not 1: wait
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+">>>>>>>A");
            number = 2;// Wake up the specified person B
            condition2.signal();/ / B to awaken
        }catch (Exception e){
            e.printStackTrace();
        }finally{ lock.unlock(); }}public void printB(a){
        lock.lock();
        try {
            while(number ! =2) {// wait if it is not 2
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+">>>>>>>B");
            number = 3;// Wake up the executor C
            condition3.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally{ lock.unlock(); }}public void printC(a){
        lock.lock();
        try {
            while(number! =3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+">>>>>>>C");
            number = 1;/ / wake A
            condition1.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally{ lock.unlock(); }}}Copy the code

Results:

T1>>>>>>>A
T2>>>>>>>B
T3>>>>>>>C
T1>>>>>>>A
T2>>>>>>>B
T3>>>>>>>C
......
Copy the code

notify()andnotifyAll()What is the difference between?

When using notify(), only one wait thread is woken up, but there is no guarantee which one. NotifyAll () wakes up all threads.

Condition than objectwait()andnotify()What are the advantages?

The question of locks can be simplified to: Who holds what locks? The owner of a lock is the thread, and the lock is held by an object or class. To wait(), the thread must wait. ,notify() is to wake up a thread, and notifyAll() is to wake up all threads. The problem is that this method has no connection to the current thread, either by leaving it waiting or waking up. The condition object can create multiple new ones, one for each synchronized block, so that it can wait to await() or wake up the thread in the synchronized block specified by signal(). Condition allows threads to execute in a specified order.

What is the 8-lock phenomenon?

Who goes first in the standard case?

Code:

public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start(); }}class Phone{
    //synchronize the lock object is the method caller!
    // Both methods use the same lock, the first to get the first to execute
    public synchronized void sendSms(a){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Texting");
    }
    public synchronized void call(a){
        System.out.println("Make a call"); }}Copy the code

Results: Text messages followed by phone calls after a 4-second delay.

Texting and callingCopy the code

Conclusion: The first to get the lock, the first to execute.

Two objects, two locks, who executes first?

The code is as follows:

public class Test2 {
    public static void main(String[] args) {
        // Two objects, two locks
        Phone2 phone = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        // Wait 1 second
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start(); }}class Phone2{
    public synchronized void sendSms(a){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Texting");
    }
    public synchronized void call(a){
        System.out.println("Make a call");
    }
    // There is no lock, it is not synchronous method, is not affected by the lock.
    public void hello(a){
        System.out.println("Hello"); }}Copy the code

Result: The call is performed after 1s, and the SMS is performed after 3s.

Call and textCopy the code

Conclusion: When two locks are used, they do not affect each other.

Two objects, call two static locking methods, call first or text first?

public class Test3 {
    public static void main(String[] args) {
        // There is only one class template for two objects
        Phone3 phone = new Phone3();
        Phone3 phone2 = new Phone3();
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start(); }}class Phone3{
    //synchronize the lock object is the method caller!
    // The class is available as soon as it is loaded. Lock is a Class
    public static synchronized void sendSms(a){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Texting");
    }
    public static synchronized void call(a){
        System.out.println("Make a call"); }}Copy the code

Results: After waiting for 4s, send text messages first and then call

Texting and callingCopy the code

Conclusion: static lock method, lock is the class, so the first to hold the lock first execute.

Two objects, call static locking method and normal locking method respectively, call first or text first?

public class Test4 {
    public static void main(String[] args) {
        // There is only one class template for two objects
        Phone4 phone = new Phone4();
        Phone4 phone2 = new Phone4();
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start(); }}class Phone4{
    // Static synchronization method
    public static synchronized void sendSms(a){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Texting");
    }
    // Common synchronization methods
    public  synchronized void call(a){
        System.out.println("Make a call"); }}Copy the code

Results: 1s after the first call, then 3s after the text message.

Call and textCopy the code

Conclusion: One locks the class and one locks the object of the class. The two locks do not affect each other.

Why is the List class unsafe?

The following code:

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    for (int i = 1; i <= 10; i++) {
        new Thread(() -> {
            list.add(UUID.randomUUID().toString().substring(0.5)); System.out.println(list); }, String.valueOf(i)).start(); }}Copy the code

Execution Result:

[null, 9c18d, d25bf, 0b19c, 8f23b, 1f7d3]
[null, 9c18d, d25bf, 0b19c, 8f23b, 1f7d3, 74ac0, 5d23a, 86246, 476b8]Exception in thread "4" 
Exception in thread "1" Exception in thread "6" [null, 9c18d, d25bf, 0b19c, 8f23b, 1f7d3]
Exception in thread "5" [null, 9c18d, d25bf, 0b19c, 8f23b, 1f7d3, 74ac0, 5d23a, 86246]
[null, 9c18d, d25bf, 0b19c, 8f23b, 1f7d3, 74ac0, 5d23a]
[null, 9c18d, d25bf, 0b19c, 8f23b, 1f7d3, 74ac0]
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
......
Copy the code

Summary: Error reported. Abnormal ConcurrentModificationException: concurrent modification.

How to solve the list class thread unsafe?

  1. Use the thread-safe Vector class
  2. Using the toolkit under Collections:List<String> list = Collections.synchronizedList(new ArrayList<>());
  3. Using the JUC package:CopyOnWriteArrayList: List<String> list = new CopyOnWriteArrayList<>();

How does CopyOnWriteArrayList work?

Add method source code:

public boolean add(E e) {
    // Reentrant lock
    final ReentrantLock lock = this.lock;
    / / acquiring a lock
    lock.lock();
    try {
        // Array of elements
        Object[] elements = getArray();
        // Array length
        int len = elements.length;
        // Copy the array
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        // Hold element e
        newElements[len] = e;
        // Set the array
        setArray(newElements);
        return true;
    } finally {
        / / releases the locklock.unlock(); }}Copy the code

Principle:

  1. Obtain the lock (to ensure the safe access of multiple threads), obtain the current Object array, obtain the Object array length is length, go to step 2.
  2. Copy an Object array with length of length+1 as newElements(newElements[length] is null) and go to the next step.
  3. Set newElements[length] to element e, and set Object[] to newElements. This completes the element addition.

What’s the advantage of CopyOnWriteArrayList over Vector?

Vector adds synchronized to all its methods to ensure synchronization, but each method has to acquire the lock when it executes, and the performance will be greatly reduced. CopyOnWriteArrayList only locks the add, delete, and change methods, but does not lock the read, so the read performance is better than Vector. CopyOnWriteArrayList supports concurrency with more reads and less writes.

Why is CopyOnWriteArrayList called CopyOnWriteArrayList

Read/write separation, copy the old one when writing, insert the value into the new one, and finally refer to the new list.

How to solve the set class thread unsafe?

Set

Set = new CopyOnWriteArraySet<>(); Using the Set < String > Set = Collections. SynchronizedSet (new HashSet < > ());

What’s underneath a HashSet?

Values in hashmap. set correspond to keys in HashMap.

What are the open address and linked list methods for resolving conflicts with hash tables?

  1. Open address method: When an address conflict occurs, the method continues to probe other storage cells in the hash table until an empty location is found.
  2. It stores a linked list (linked list + red-black tree after jdk1.8) where conflicts occur, where all synonym records are stored

HashMaphashCode()What does the method do?

The hashCode() method determines which bucket the object will be placed in

HashMapequals()What does the method do?

When multiple objects have conflicting hash values, the equals() method determines whether they are “the same object.”

Why is the capacity of a HashMap an integer power of 2?

If n is a multiple of 2, then n minus 1 is an odd number, and the last digit of an odd number must be 1. If n is not an integer power of 2, then the hash (n-1) &hash will always have the last bit of 0, so the subscript will always be even. All data can only be stored in even numbers.

What is the| =?

Similar to + =, open is a = a | b, computation rules: two binary corresponding to zero at the same time, the result is 0, or 1.

What is the& =?

The result is 1 if the corresponding binary bits are 1 at the same time, and 0 otherwise.

What is the^ =?

The corresponding binary bits are 0 at the same time; otherwise, 1.

<< >> >>>What are the operations?

  1. <<, move to the left and fill the low position with 0
  2. >>, right shift, if the number is positive, the highest complement 0, if the number is negative, the highest complement 1.
  3. >>>, unsigned shift to the right, regardless of the number positive or negative, high order complement 0.

Given a positive integer value, how do I get the next NTH power of 2?

1. Add 1 to the HashMap.

private static final int tableSizeFor(int c) {
    int n = c - 1;// start with -1 to prevent carry
    n |= n >>> 1;// Move it 1 bit to the right, then press bit or, so that the highest bit and the second highest bit are both 1.
    n |= n >>> 2;// Move 2 to the right, then press or, so the first four digits are 1
    n |= n >>> 4;// The first eight bits are 1
    n |= n >>> 8;// The first 16 bits are 1
    n |= n >>> 16;// the first 32 bits are all 1. Since int is only 32 bits at most, all the units bits are 1 from the highest bit
    return (n < 0)?1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;// If the maximum space is not exceeded, then +1 becomes the next 2 to the NTH power.
}
Copy the code

Why is the capacity of a HashMap an integer power of 2?

  1. Hash method: A method that converts the key object to an integer
  2. IndexFor: convert an integer from a hash to an array of linked lists.
static int indexFor(int h, int length) {
    return h & (length-1);
}
Copy the code

Explanation: The parameter h is the integer obtained by the hash method, and length is the capacity of the hashMap, which is the n power of 2. Length-1 is to obtain a binary number with 1 bits less than the capacity, and then sum with the integer H by bits. In fact, it is to remove the part with the largest bit more than the capacity and retain the part with the smallest bit more than the capacity, which is mod. So this is efficient mod. That is X % 2^n = X & (2^n – 1). Example: 10&7=2

001010&000111 = 000010Copy the code

Summary: In order to improve the efficiency of module fetching, JDK engineers use bitwise operation instead of module fetching, which requires that the capacity of Map must be an integer power of 2.

Explore the HashMap data structure and the integer power of 2

How does ConcurrentHashMap work?

ConcurrentHashMap: ConcurrentHashMap and Hashtable

How does a Callable compare to a Runnable?

  1. Callable has a return value
  2. Callable can throw exceptions
  3. The method I need to rewrite iscall()methods

How many times do two threads execute a callable?

1. Results are cached.

How do I use Callable?

Put callable objects in futureTask, which also implements the Runnable interface and therefore can be executed directly in Thread. The code is as follows:

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // How to start callable?
        1. Create a callable object
        MyThread myThread = new MyThread();
        //2. Convert the Callable object to futureTask. Because futureTask implements both the Callable and runnable interfaces
        FutureTask futureTask = new FutureTask(myThread);
        // Put the futureTask into the Thread object
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();// The results are cached to improve efficiency
        // It may block, put it last to fetch
        // Or use asynchronous communication for processingInteger result = (Integer) futureTask.get(); System.out.println(result); }}/ / callable inheritance
class  MyThread implements Callable<Integer> {
    @Override
    public Integer call(a) throws Exception {
        Thread.sleep(2000);
        System.out.println("To call > > > > > > > > > > > >");
        return 1024; }}Copy the code

What does CountDownLatch do?

The subtraction counter is used to wait until the thread has finished executing before continuing to execute.

What does CountDownLatch work on?

What does a CyclicBarrier do?

Similar to an addition counter. A thread executes somewhere and waits, and a counter increases to a specified value to perform a specific action. Threads can wait repeatedly. The code is as follows:

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // Collect 7 dragon balls to summon the divine dragon
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println(Thread.currentThread().getName()+"Summon the Dragon successfully.");
        });
        for(int i=1; i<=7; i++){// Can lamba expressions operate on I?
            int finalI = i;
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"Collected"+finalI+"Dragon Ball");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName()+"Collected"+finalI+The dragon pig);
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName()+"The dragon and the pig are invincible.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch(BrokenBarrierException e) { e.printStackTrace(); } }).start(); }}}Copy the code

Execution Result:

Thread-1A collection of2Dragon Thread -3A collection of4Dragon Thread -0A collection of1Dragon Thread -2A collection of3Dragon Thread -4A collection of5Dragon Thread -5A collection of6Dragon Thread -6A collection of7Dragon Thread -6Summon Dragon Thread- successfully6A collection of7Single Thread - dragon pig1A collection of2Single Thread - dragon pig4A collection of5Single Thread - dragon pig2A collection of3Single Thread - dragon pig5A collection of6Single Thread - dragon pig0A collection of1Single Thread - dragon pig3A collection of4Single Thread - dragon pig3Summon Dragon Thread- successfully3, dragon pig collection, invincible Thread-1, dragon pig collection, invincible Thread-4, dragon pig collection, invincible Thread-5, dragon pig collection, invincible Thread-0, dragon pig collection, invincible Thread-6, dragon pig collection, invincible Thread-2Dragon pig collection, the world is invincibleCopy the code

The differences between countdownLatch and cyclicBarrier are summarized as follows:

  1. CountdownLatch is a subtraction and cyclicBarrier is an addition
  2. The countdownLatch requires the code to perform the subtraction calculation manually and the cyclicBarrier thread to perform the addition count automatically when it arrives at the await() method
  3. CyclicBarrier can specify what to do after the thread reaches await(),countdownLatch cannot.

What does a CyclicBarrier work on?

What does Semaphore do?

Semophore is similar to a resource lock, synchronize or lock, but allows more than one thread to enter the lock. The code is as follows:

public class SemophoreDemo {
    public static void main(String[] args) {
        // Number of threads: parking Spaces
        Semaphore semaphore = new Semaphore(3);
        for(int i=1; i<=6; i++){new Thread(()->{
                / / acquire ()
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"Grab a parking space.");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"Leave the parking space.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    / / release ()semaphore.release(); } },String.valueOf(i)).start(); }}}Copy the code

Execution Result:

2 grab a parking space 1 grab a parking space 3 grab a parking space 2 leave a parking space 1 leave a parking space 3 leave a parking space 6 grab a parking space 4 grab a parking space 5 grab a parking space 6 grab a parking spaceCopy the code

How does Semaphore work?

What is a read/write lock?

Read – read coexistence, read – write coexistence, write – write coexistence.

How to use read/write lock?

  1. Define a lock
  2. You can choose to read or write the lock when using it.
  3. manuallock()andunlock()The code is as follows:
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache2 myCache = new MyCache2();
        / / write
        for (int i = 1; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.put(finalI + "", finalI + "");
            }, String.valueOf(i)).start();
        }
        / / read
        for (int i = 1; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.get(finalI + "");
            }, String.valueOf(i)).start();
        }
        / / read 2
        for (int i = 1; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.get2(finalI + ""); }, String.valueOf(i)).start(); }}}/** * custom cache, lock */
class MyCache2 {
    private volatile Map<String, Object> map = new HashMap<>();
    // Read/write locks, more fine-grained operations
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // Only one thread can write at the same time
    public void put(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println(Thread.currentThread().getName() + "Written" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "Write complete");
        } catch (Exception e) {
            e.printStackTrace();
        } finally{ readWriteLock.writeLock().unlock(); }}/ / get read
    public Object get(String key) {
        readWriteLock.readLock().lock();
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println(Thread.currentThread().getName() + "Read" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "Read complete" + o);
            return o;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
        return null;
    }
    / / get read
    public Object get2(String key) {
        readWriteLock.readLock().lock();
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println(Thread.currentThread().getName() + "Read the = = =" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "Read === end" + o);
            return o;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
        return null; }}Copy the code

Results:

1 to 1 1 finished writing three write 3 3 finished writing 4 write 4 4 finished write write 2 2 finished write 5 write 5 5 write to complete 6 write 6 finished write write 7 July 7 after finished write write 8 August 8 to 9 written 9 9 written over 6 read 6 6 read finished reading = = = 6 6 Read === end 6 9 Read ===9 9 Read === end 9 5 Read ===5 5 Read ===2 2 Read === end 2 8 Read ===8 8 Read === end 8 3 Read 3 7 Read ==7 7 Read === end 7 7 Read == end 7 7 Read == End 7 7 Read == End 7 7 Read == End 3 Read data ===3 3 Read data === end 3 1 Read data ===1 1 Read data === end 1 4 Read data === end 4 9 Read data 9 9 Read data 9 5 Read data 5 4 Read data 4 Read data 4 1 Read data 1 1 Read data 8 8 Read data 8 2 Read data 2 2 Read out 2 3 read out 3Copy the code

Conclusion: The lock is held while writing, so output results every 1 second. After output, when read, all results are output together. Note Reading and writing cannot coexist, but reading can coexist.

What are shared locks and exclusive locks?

Can I read simultaneously with a read/write lock? Can read and write coexist? Can you write both?

You can read at the same time, you can’t read and write at the same time.

What is a blocking queue?

  1. Queue full blocks queue entry
  2. An empty queue blocks an unqueued operation

When are blocking queues used?

  1. Producer-consumer
  2. The thread pool

What is a AbstractQueue?

What is the method by which ArrayBlockingQueue throws an exception?

Add (increase) | remove (delete) | element (see the team first element)

What is the method of ArrayBlockingQueue that does not throw an exception?

offer|poll|peek

What is the method by which ArrayBlockingQueue keeps blocking?

put|take

What is the blocking method for ArrayBlockingQueue timeout exit?

offer("d", 2, TimeUnit.SECONDS)|poll(2,TimeUnit.SECONDS)

What is a SychronizeQueue?

Synchronous queue. is a queue that does not store elements. , take the element and hand it directly to the consumer. The code is as follows:

public class SychronizeQueueDemo {
    public static void main(String[] args) {
        SynchronousQueue<String> queue = new SynchronousQueue<>();
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + "put 1");
                queue.put("1");
                System.out.println(Thread.currentThread().getName() + "put 2");
                queue.put("2");
                System.out.println(Thread.currentThread().getName() + "put 3");
                queue.put("3");
            } catch(InterruptedException e) { e.printStackTrace(); }},"T1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "take" + queue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "take" + queue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()  + "take" + queue.take());
            } catch(InterruptedException e) { e.printStackTrace(); }},"T2").start(); }}Copy the code

The results are as follows:

T1put 1
T2take1
T1put 2
T2take2
T1put 3
T2take3
Copy the code

Summary: Display put after running, take and put after 1s, take and put after 2s, and take after 3s. Note: If there is no take, the put operation will block the wait, and if there is no put, the take operation will block the wait.

Is sychronize Ue secure in multithreading?

What is pooling technology?

Is to save some resources in advance for a rainy day.

  1. Thread pool: start a number of threads, and let these threads sleep, when the client has a new request, will wake up a sleeping thread in the thread pool, let it handle the client’s request, when the request is finished, the thread goes to sleep again.
  2. Memory pooling: Pre-allocating enough memory to form a preliminary “memory pool.” Freeing memory is the process of putting it back into the pool, not actually freeing or deleting it. Those who put memory into the memory pool should make the flag position free and the pool should be destroyed when the application terminates. The main job is to free each chunk of memory in the memory pool.
  3. Database connection pooling: Database connections are a critical limited and expensive resource. Database connection pooling is the pooling of sufficient database connections at application startup and the pooling of these links into a connection pool. With the application of dynamic connection in the pool is to apply dynamic increase | | to reduce the number of connections in the pool.

What pools are there?

  1. The thread pool
  2. Memory to eat
  3. Database connection pool
  4. Object pooling

What are the benefits of thread pools?

  1. Reduce resource consumption
  2. Improve response speed
  3. Convenient management

What are the top 3 methods for thread pooling?

  1. Executors.newSingleThreadExecutor();// Generate a single thread pool
  2. Executors.newFixedThreadPool(5);// Generate a fixed size thread pool
  3. Executors.newCachedThreadPool();// Generate a scalable thread pool

What are the pitfalls of FixedThreadPool and SingleThreadPool?

The allowed queue length is integer. MAX_VALUE, which may accumulate a large number of requests, causing OOM.

What are the pitfalls of CachedThreadPool and ScheduleThreadPool?

The number of threads allowed to be created is integer.max_value, which may create a large number of threads, resulting in OOM.

What are the seven main parameters of a thread pool?

  1. int corePoolSize// The core thread pool size, the core thread will always exist
  2. int maximumPoolSize// Maximum core thread pool size, the maximum number of threads allowed in the thread pool
  3. long keepAliveTimeThe number of threads exceeds the core thread pool size, and the thread is terminated after this time.
  4. TimeUnit unit// Timeout unit
  5. BlockingQueue<Runnable> workQueue// Block the queue where the task is stored until it is executed by the thread.
  6. ThreadFactory threadFactory// Thread factory: creates the thread, generally does not move. Called when the thread is created
  7. RejectedExecutionHandler handle// Reject policy, the execution policy when the number of tasks reaches the maximum capacity of the queue. The code is as follows:
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
Copy the code

What are the four rejection strategies for thread pools?

  1. new ThreadPoolExecutor.DiscardOldestPolicy()// Discard the earliest unprocessed task and try again. Not throwing an exception
  2. new ThreadPoolExecutor.DiscardPolicy// Quietly discard the task without throwing an exception
  3. new ThreadPoolExecutor.AbortPolicy/ / discard task, and throws an exception: RejectedExecutionException
  4. new ThreadPoolExecutor.CallerRunsPolicy// Execute the task directly in the thread that called the method (as if there were no thread pool) without throwing an exception

What are CPU-intensive tasks?

A large number of computing tasks, such as: calculate PI | to high-definition video decoding and so on, this computationally intensive task although can use many task to complete, but the task, the more the more time spent on the task switching, the lower the efficiency of the CPU to perform a task. So for efficient CPU utilization, the number of computationally intensive tasks running simultaneously should equal the number of CPU cores.

What are IO intensive tasks?

Task involves network | disk IO are IO intensive tasks, the characteristics of this kind of task is very little CPU consumption, task is waiting for IO operation to complete most of the time. For IO intensive tasks, the more tasks, the higher THE CPU efficiency. Most common tasks are IO intensive, such as Web applications.

How do you define a maximum thread?

How do I customize the thread pool?

The code is as follows:

public class Demo1 {
    public static void main(String[] args) {
// ExecutorService executorService = Executors.newSingleThreadExecutor(); // Single thread
// ExecutorService executorService = Executors.newFixedThreadPool(5); // Create a thread pool of fixed size
// ExecutorService executorService = Executors.newCachedThreadPool(); // Retractable, strong when strong, weak when weak
        // How to define the maximum thread
        // 1. CPU intensive, defined as several cores
        // 2. I/o intensive
        ExecutorService executorService = new ThreadPoolExecutor(2.5.3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
// new ThreadPoolExecutor.AbortPolicy()); // Reject with an error
// new ThreadPoolExecutor.CallerRunsPolicy()); // Go back to where you came from
// new ThreadPoolExecutor.DiscardPolicy()); // If the queue is full, no exception will be thrown when the task is dropped
                new ThreadPoolExecutor.DiscardOldestPolicy());// The queue is full, trying to compete with the earliest one without throwing an exception
        try {
            for (int i = 0; i < 9; i++) {
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" ok"); }); }}catch (Exception e) {
            e.printStackTrace();
        } finally {
            // The thread pool is used up, the program ends, close the thread poolexecutorService.shutdown(); }}}Copy the code

Execution Result:

pool-1-thread-1 ok
pool-1-thread-4 ok
pool-1-thread-4 ok
pool-1-thread-3 ok
pool-1-thread-2 ok
pool-1-thread-4 ok
pool-1-thread-1 ok
pool-1-thread-5 ok
Copy the code

Analysis: When the number of core threads in the thread pool exceeds, the maximum number of threads to execute the task is enabled.

What are the four programming styles that the new generation of programmers must master?

  1. Lambda expressions
  2. Chain programming
  3. Functional interface
  4. Stream computing

What are the four functional interfaces?

  1. Supplier
  2. Consumer
  3. Predicate
  4. Function

What is a functional interface?

There is and only one abstract method.

What are functional interfaces for?

What is Stream computing?

Flow only represent the data flow, and no data structure, its source can be Collection | array | IO, etc. Streams provide an interface to manipulate big data, making it easier and faster to manipulate data. It has methods of filtering/mapping and reducing the number of traversals. Its method is divided into: intermediate method and terminal method. The intermediate method always returns a Stream.

What are the methods for Stream?

  1. Stream<T> filter(Predicate<? super T> predicate): intermediate method that returns a stream that matches the condition
  2. Stream<R> map(Function<? super T, ? extends R> mapper): Applies methods to elements in the flow and returns a new flow
  3. Stream<T> distinct();: Returns a stream containing different elements. For ordered flows, the first occurrence of the element is always retained in the different elements.
  4. Stream<T> sorted();: Natural sort
  5. Stream<T> sorted(Comparator<? super T> comparator);: sort using the comparator passed in.
  6. Stream<T> peek(Consumer<? super T> action);
  7. Stream<T> limit(long maxSize);: truncates the element in the stream to a length not larger than maxSize.
  8. Stream<T> skip(long n);: Drops the first element and returns the stream. If there is only one element, an empty stream is returned.
  9. void forEach(Consumer<? super T> action);: Performs operations on each element in the flow
  10. Object[] toArray();: Returns an array containing elements from the stream
  11. T reduce(T identity, BinaryOperator<T> accumulator);:
  12. <R, A> R collect(Collector<? super T, A, R> collector);:
  13. Optional<T> min(Comparator<? super T> comparator);:

How do you do streaming?

What is Optional?

Optional is a wrapper class. A layer of NULL is wrapped around NULL to prevent NULL pointer exceptions from being reported for NULL operations.

What are Optional’s methods?

  1. How to get an Optional instance: 1.1.public static <T> Optional<T> empty(): Gets an Optional empty instance 1.2.public static <T> Optional<T> of(T var0)If var0 is null, the null pointer is 1.3.public static <T> Optional<T> ofNullable(T var0)If var0 is null, an Optional empty instance is returned.
  2. Check whether Optional exists? 2.1.public boolean isPresent(): Returns whether the value in the Optional instance is NULL 2.2.public void ifPresent(Consumer<? super T> var1): If value is not NULL, var1 is executed
  3. Determine whether it is equal 3.1.public boolean equals(Object var1)If the value in the: optional instance is NULL, the comparison is normal.

How to use Optional?

The code is as follows:

public class OptionalTest {
    static List<User> list = new ArrayList<>();
    public static void main(String[] args) {
        User user1 = new User("1"."guohao".11);
        User user2 = new User("2"."liqin".10);
        list.add(user1);
        list.add(user2);
        Optional<User> user3 = Optional.ofNullable(getUserById("1"));
        user3.ifPresent(u-> System.out.println(u.getName()));
        Optional<User> user4 = Optional.ofNullable(getUserById("3"));
        user4.ifPresent(u-> System.out.println(u.getName()));
    }
    public static User getUserById(String id){
        return list.stream().filter(u -> u.getId() == id).findAny().orElse(newUser()); }}Copy the code

Summary: Optional can effectively reduce null pointer judgments. It is very neat to use with a Stream Stream. Null (Optional) Null (Optional) Null (Optional

What is a Function interface?

Function

: V represents the input parameter and R represents the result returned. This functional interface is equivalent to y=f(x).
,>

  1. R apply(T t);// Apply the Function object to the input parameter and return the calculated result.
  2. Function<T, V> andThen(Function<? super R, ? extends V> after)// Returns a function object that executes the apply method of the current function object before the apply method of the after function object.return (T t) -> after.apply(apply(t));
  3. Function<V, R> compose(Function<? super V, ? extends T> before)// Returns a function object that executes before apply and then the current apply method.return (V v) -> apply(before.apply(v));The code is as follows:
public class Demo01 {
    public static void main(String[] args) {
        Function<Integer, Integer> name = e -> e * 2;
        Function<Integer, Integer> square = e -> e * e;
        int value = name.andThen(square).apply(3);/ / 36
        System.out.println("andThen value=" + value);
        int value2 = name.compose(square).apply(3);/ / 18
        System.out.println("compose value2=" + value2);
        // Returns a function object that only returns input parameters after applying ()
        Object identity = Function.identity().apply("huohuo"); System.out.println(identity); }}Copy the code

Results:

andThen value=36
compose value2=18
huohuo
Copy the code

Summary: andThen() will apply first andThen populate the result into the caller’s apply method execution. Compose () will call the function method in compose() first and populate the result into the apply method

What is the Predicate interface?

An interface with an input parameter that returns true or false.

  1. boolean test(T t);
  2. Predicate<T> and(Predicate<? super T> other)//return (t) -> test(t) && other.test(t);
  3. Predicate<T> negate()//return (t) -> ! test(t);
  4. Predicate<T> or(Predicate<? super T> other)//return (t) -> test(t) || other.test(t);

What is the Consumer interface?

A method has only incoming arguments and no returns.

  1. void accept(T t);// Execute method
  2. Consumer<T> andThen(Consumer<? super T> after)//return (T t) -> { accept(t); after.accept(t); };Execute the accept method before executing itandThen()The method code is as follows:
public class Demo03 {
    public static void main(String[] args) {
        testAndThen();
    }
    public static void testAndThen(a){
        Consumer<Integer> consumer1 = x -> System.out.println("first x : " + x);
        Consumer<Integer> consumer2 = x -> {
            System.out.println("second x : " + x);
        };
        Consumer<Integer> consumer3 = x -> System.out.println("third x : " + x);
        consumer1.andThen(consumer2).andThen(consumer3).accept(1); }}Copy the code

Output result:

first x : 1
second x : 1
third x : 1
Copy the code

When used together with andThen, the accept entry is used as a common entry parameter. Accept is executed first, followed by andThen in sequence.

What is the Supplier interface?

Method only returns, no input arguments. There is only one get method T get(); Code examples:

Supplier<String> supplier = ()->{return "hehe"; }; System.out.println(supplier.get());Copy the code

The code is as follows:

public static void main(String[] args) {
    Predicate<String> predicate = str->{return str==null|| str.isEmpty(); }; System.out.println(predicate.test("123"));
}
Copy the code

join(long millis)How does it work?

The join waits at most seconds until the thread calling the join dies. NotifyAll is called when a thread terminates to wake up the other threads.

What is the double colon operator?

The double colon (::) operator is used as a method reference in Java 8 Method references are an important feature associated with lambda expressions that provide a method that does not execute a method. A lambda is an anonymous calling method, and a double colon is a name that calls an existing method.

  1. Static method references:classname::methodname, such as:Person::getAge
  2. Object instance method reference:instancename::methodname, such as:System.out::println
  3. Object superclass method reference:super::methodname
  4. Class constructor reference:classname:new, such as:Arraylist::new
  5. Array constructor references:typename[]::new, such as:String[]::new

What is a ForkJoin?

The essence is a framework for parallel task execution, which can divide a large task into several small tasks, and finally summarize the results of each small task to obtain the calculation results of the large task. The ForkJoin framework is a new feature introduced in Jdk 1.7 that, like ThreadPoolExecutor, implements the Executor and ExecutorService interfaces. It uses an infinite queue to hold tasks that need to be executed, and the number of threads is passed in through the constructor. If none is passed in, it defaults to the number of cpus available on the current computer.

What is job theft?

In order to reduce the competition between threads, we put the subtasks in different queues and create a separate thread for each queue to execute the tasks in the queue, thread and queue one by one. However, some thread will finish the task in its own queue while other threads have other tasks waiting to be processed in their corresponding queue, so it will steal a task from another thread’s queue to execute. To reduce contention between the stolen and stolen task threads, a double-endian queue is usually used, where the stolen thread always takes the task from the head of the double-endian queue, and the stolen thread always takes the task from the tail of the double-endian queue.

How do I use ForkJoin?

  1. Create a new class and inheritRecursiveTaskClass.
  2. rewritecomputeMethod, the method to write merge branch calculation rules. useforkLet’s say a task joins a thread queue.
  3. Using ForkJoinPool, using merge branch calculation. The code is as follows:
public class ForkJoinDemo extends RecursiveTask<Long> {
    private long start;
    private long end;
    private long temp = 10000L;

    public ForkJoinDemo(long start, long end) {
        this.start = start;
        this.end = end;
    }
    @Override
    protected Long compute(a) {
        if ((end - start) < temp) {
            Long sum = 0L;
            for(Long i=start; i<=end; i++){ sum += i; }return sum;
        } else {
            // branch merge calculation
            long middle = (end + start)/2;/ / intermediate value
            ForkJoinDemo task1 = new ForkJoinDemo(start,middle);
            task1.fork();// Split the task and push it into a thread queue
            ForkJoinDemo task2 = new ForkJoinDemo(middle+1,end);
            task2.fork();// Split the task and push it into a thread queue
            returntask1.join()+task2.join(); }}}Copy the code
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
// test1(); / / 6460
        test2();/ / 4491
// test3(); / / 205
    }
    // Common programmer
    public static void test1(a){
        long start = System.currentTimeMillis();
        Long sum = 0L;
        for(Long i=1L; i<=10 _0000_0000; i++){ sum += i; }long end = System.currentTimeMillis();
        System.out.println("sum = "+sum+"Time."+(end-start));
    }
    ForkJoin is used
    public static void test2(a) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(1.10 _0000_0000);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();
        long end = System.currentTimeMillis();
        System.out.println("sum = "+sum+"Time."+(end-start));
    }
    // Use parallel flow computation
    public static void test3(a){
        long start = System.currentTimeMillis();
        / / the stream parallel flows
        long sum = LongStream.rangeClosed(0.10 _0000_0000).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum = "+sum+"Time."+(end-start)); }}Copy the code

Summary: ForkJoin can be used to improve efficiency, and parallel flow can be used to improve efficiency.

What is the difference between RecursiveAction and RecursiveTask?

It’s a recursive way of doing things.

  1. RecursiveAction returns no value and implements the Runnable interface
  2. RecursiveTask returns a value that implements the Callable interface.

What is a CompletableFuture?

New classes in JDK 1.8. The Future extension simplifies asynchronous programming and provides functional programming capabilities that can handle computations with callbacks, as well as transform and combine CompletableFuture methods.

Why add the CompletableFuture class?

JDK 1.5 added the Future interface, which provides the ability to perform tasks asynchronously. But for the results of the acquisition is very inconvenient, only through the blocking or polling way to get the results of the task, the blocking way is obviously against the original intention of asynchronous programming, polling way will consume CPU resources. The CompletableFuture class still supports blocking and polling of results through the CompletionStage and Future interfaces. You can also specify a callback function after a calculation. For example CompletableFuture. SupplyAsync (this: : sendMsg). ThenAccept (this: : notify);

How do I use CompletableFuture to implement an asynchronous callback task?

The following code demonstrates how this works as follows:

public class Main {
    public static void main(String[] args) throws Exception {
        // Create asynchronous execution task:
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(Main::fetchPrice);
        // If executed successfully:
        cf.thenAccept((result) -> {
            System.out.println("price: " + result);
        });
        // If the execution is abnormal:
        cf.exceptionally((e) -> {
            e.printStackTrace();
            return null;
        });
        System.out.println("> > > > > > > > >");
        // The main thread should not end immediately, otherwise the thread pool used by CompletableFuture by default will be closed immediately:
        Thread.sleep(100);
        System.out.println("< < < < < < < < <");
    }
    static Double fetchPrice(a) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        if (Math.random() < 0.3) {
            throw new RuntimeException("fetch price failed!");
        }
        return 5 + Math.random() * 20; }}Copy the code

Execution Result:

>>>>>>>>>
price: 20.583016768717513
<<<<<<<<<
Copy the code

Code 2 is as follows:

public class Main2 {
    public static void main(String[] args) throws Exception {
        // First task:
        CompletableFuture<String> cfQuery = CompletableFuture.supplyAsync(() -> {
            return queryCode("Petrochina");
        });
        // cfQuery succeeds and executes the next task:
        CompletableFuture<Double> cfFetch = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice(code);
        });
        // cfFetch successfully prints the result:
        cfFetch.thenAccept((result) -> {
            System.out.println("price: " + result);
        });
        // The main thread should not end immediately, otherwise the thread pool used by CompletableFuture by default will be closed immediately:
        Thread.sleep(2000);
    }
    static String queryCode(String name) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        return "601857";
    }
    static Double fetchPrice(String code) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        return 5 + Math.random() * 20; }}Copy the code

Results:

Price: 21.408665476262335Copy the code

Multiple CompletableFuture serial processing codes are as follows:

public class Main3 {
    public static void main(String[] args) throws Exception {
        // Two CompletableFutures perform an asynchronous query:
        CompletableFuture<String> cfQueryFromSina = CompletableFuture.supplyAsync(() -> {
            return queryCode("Petrochina"."https://finance.sina.com.cn/code/");
        });
        CompletableFuture<String> cfQueryFrom163 = CompletableFuture.supplyAsync(() -> {
            return queryCode("Petrochina"."https://money.163.com/code/");
        });
        // Merge anyOf into a new CompletableFuture:
        CompletableFuture<Object> cfQuery = CompletableFuture.anyOf(cfQueryFromSina, cfQueryFrom163);
        // Two CompletableFutures perform an asynchronous query:
        CompletableFuture<Double> cfFetchFromSina = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice((String) code, "https://finance.sina.com.cn/price/");
        });
        CompletableFuture<Double> cfFetchFrom163 = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice((String) code, "https://money.163.com/price/");
        });
        // Merge anyOf into a new CompletableFuture:
        CompletableFuture<Object> cfFetch = CompletableFuture.anyOf(cfFetchFromSina, cfFetchFrom163);
        // Final result:
        cfFetch.thenAccept((result) -> {
            System.out.println("price: " + result);
        });
        // The main thread should not end immediately, otherwise the thread pool used by CompletableFuture by default will be closed immediately:
        Thread.sleep(200);
    }
    static String queryCode(String name, String url) {
        System.out.println("query code from " + url + "...");
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
        }
        return "601857";
    }
    static Double fetchPrice(String code, String url) {
        System.out.println("query price from " + url + "...");
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
        }
        return 5 + Math.random() * 20; }}Copy the code

The results are as follows

query code from https://finance.sina.com.cn/code/...
query code from https://money.163.com/code/...
query price from https://finance.sina.com.cn/price/...
query price from https://money.163.com/price/...
price: 21.516033760587096
Copy the code

To sum up: CompletableFuture can process tasks asynchronously, and can implement asynchronous completion by automatically calling callback functions. ThenAccept () handles normal results; Exceptional () handles exception results; ThenApplyAsync () is used to serialize another CompletableFuture; AnyOf () and allOf() are used to parallelize multiple CompletableFutures.

What is a volatile?

Lightweight synchronization mechanism provided by the Java VIRTUAL machine.

What does volatile do?

  1. Guaranteed visibility
  2. Atomicity is not guaranteed
  3. Disallow instruction reordering

What is the JMM?

The Java memory model is a concept.

Some synchronization conventions for the JMM?

  1. Shared variables must be flushed back to main memory immediately before the thread is unlocked.
  2. Before a thread locks, it must read the latest value from main memory into working memory.
  3. Locking and unlocking are the same lock.

What are the eight operations of the JMM?

  1. Lock: lock. A variable acting on main memory that identifies a variable as thread-exclusive.
  2. Unlock: unlocked. A variable that acts on main memory, freeing a variable that is in the locked state before it can be locked by other threads.
  3. Read: read. A variable acting on main memory that transfers the value of a variable from main memory to the thread’s working memory for subsequent load action.
  4. The load, load. A variable operating on working memory that puts read operations from variables in main memory into working memory.
  5. Use: use. Function on a variable in working memory, which transfers the variable in working memory to the execution engine and is used whenever the virtual machine reaches a value that requires the variable.
  6. The assign: replication. Acts on a variable in working memory, which puts a value received from the execution engine into a copy of the variable in working memory.
  7. Store: store. A variable acting on main memory that passes a value from a variable in working memory to main memory for subsequent write use.
  8. Write: write. Applied to a variable in main memory, which puts the value of a variable in main memory that the store operation fetched from working memory.

What are the rules for JMM8 instructions?

  1. Read and load operations are not allowed. Store and write operations are not allowed. Read must load and write must store.
  2. A thread is not allowed to discard its most recent assign operation, which means it must inform main memory of any data changes to a variable in working memory.
  3. A thread is not allowed to synchronize unassigned data from working memory back to main memory.
  4. A new variable must be created in main memory. Working memory is not allowed to use an uninitialized variable directly. The use and sotre operations must be assign and load before the use and sotre operations.
  5. Only one thread can lock a variable at a time. You must unlock the device for the same number of times.
  6. If a variable is locked, all of its values in working memory will be emptied. Before the execution engine can use the variable, the variable must be reloaded or assigned to initialize the value.
  7. You cannot unlock a variable if it is not locked. You cannot unlock a variable that is locked by another thread
  8. Before an UNLOCK operation can be performed on a variable, it must be synchronized back to main memory

How do I verify the visibility of volatile?

Without volatile, the thread does not know that the value in the main thread has changed. The code is as follows:

public class JMMDemo {
    // If not volatile, the program will loop forever
    // Volatile ensures program visibility
    private volatile static int num = 0;
    public static void main(String[] args) throws InterruptedException {/ / main thread
        new Thread(()->{Thread 1 is unaware of the main memory changes
            while (num == 0){
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        num = 1; System.out.println(num); }}Copy the code

Why does volatile not guarantee atomicity?

Because volatile is not locked. The code is as follows:

public class VDemo02 {
    private static volatile int num = 0 ;// Volatile does not guarantee correctness
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(20);
        for (int i = 0; i <20 ; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
                countDownLatch.countDown();
            }).start();
        }
        countDownLatch.await();
        System.out.println(num);// The theoretical result is 20000
    }
    public  static void add(a){ num ++; }}Copy the code

Actual results:

19436
Copy the code

Summary: Volatile does not guarantee correct results for nonatomic operations. Similar to num++ operations, atomic classes can be used to ensure that the result is correct.

What is order reordering?

On the premise of ensuring the correct execution result of single thread, the operating system will rearrange the instructions without data dependence to improve the execution efficiency. Instruction reordering is: source code -> compiler optimization reordering -> instruction parallel reordering -> memory system reordering -> Execution example:

boolean contextReady = false;
// Execute in thread A:
context = loadContext();
contextReady = true;
// Execute in thread B:
while(! contextReady ){ sleep(200);
}
doAfterContextReady (context);
Copy the code

The instructions may be rearranged to read:

boolean contextReady = false;
// Execute in thread A:
contextReady = true;
context = loadContext();
// Execute in thread B:
while(! contextReady ){ sleep(200);
}
doAfterContextReady (context);
Copy the code

At this point, it is likely that the context has not been loaded, the contextReady variable has been set to true, and thread B simply jumps out of the loop and starts executing the doAfterContextReady method, resulting in an error.

What is a memory barrier?

Memory barriers cause the CPU or compiler to impose a sort constraint on memory operations issued before and after barrier instructions, meaning that operations issued before the barrier are guaranteed to be executed before operations issued after the barrier. A memory barrier is a barrier between instructions that should be executed sequentially but may be switched for efficiency. Volatile variables add a barrier before and after their memory operations.

How to use the singleton pattern of double detection?

public class LazyMan {
    // Be sure to use volatile to prevent uninitialized objects from escaping
    private volatile static LazyMan lazyMan;
    private LazyMan(a){
        System.out.println(Thread.currentThread().getName()+" OK");
    }
    // Dual detection mode
    public static LazyMan getInstance(a){
        if(lazyMan == null) {// If already initialized, return directly
            synchronized (LazyMan.class){
                if(lazyMan == null) {/** * 1. Allocate memory * 2. Execute constructor, initialize object * 3. Point this object to the space */
                    lazyMan = new LazyMan();// It is not atomic and must be volatile to prevent reordering}}}return lazyMan;
    }
    // Break the singleton pattern with reflection
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        LazyMan lazyMan = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); LazyMan lazyMan1 = declaredConstructor.newInstance(); System.out.println(lazyMan); System.out.println(lazyMan1); }}Copy the code

Is it safe to use lazy singleton mode with double detection?

It’s not safe. It could be broken by reflection. The implementation comparison of various singleton patterns is as follows:

What is enum?

Enum is essentially a class that inherits java.lang.Enum

. Can be used as a constant class.

How to use Enum to implement singleton pattern?

The code for implementing the singleton pattern using Enum in a class is as follows:

public class User {
    // Privatize constructor
    private User(a){}// Define a static enumeration class
    static enum SingletonEnum{
        // Create an enumeration object that is inherently singleton
        INSTANCE;
        private User user;
        // Private enumeration constructor
        private SingletonEnum(a){
            user=new User();
        }
        public User getInstnce(a){
            returnuser; }}// Expose a static method to get the User object
    public static User getInstance(a){
        returnSingletonEnum.INSTANCE.getInstnce(); }}class Test1 {
    public static void main(String [] args){ System.out.println(User.getInstance()); System.out.println(User.getInstance()); System.out.println(User.getInstance()==User.getInstance()); }}Copy the code

The test results are as follows:

com.guohao.pc.single.User@1540e19d
com.guohao.pc.single.User@1540e19d
true
Copy the code

Enum implements a singleton pattern that is indeed thread-safe and prevents reflection from breaking. The disadvantage is that lazy loading is not possible.

How to use Enum?

Use Enum as a constant as follows:

public enum Color {
    RED("Red".1), GREEN("Green".2), BLANK("White".3), YELLO("Yellow".4);
    // Member variables
    private String name;
    private int index;
    // constructor
    private Color(String name, int index) {
        this.name = name;
        this.index = index;
    }
    // Common method
    public static String getName(int index) {
        for (Color c : Color.values()) {
            if (c.getIndex() == index) {
                returnc.name; }}return null;
    }
    // get set method
    public String getName(a) {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getIndex(a) {
        return index;
    }
    public void setIndex(int index) {
        this.index = index; }}Copy the code

Java enum

Does an enumerated class have a no-argument constructor?

No, there is only one constructor with two arguments. For example, looking at the source code for an enumeration class, the private constructor looks like this:

// Enumeration constructor, can only be called by the compiler
protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}
Copy the code

Learn more about Java enumeration types (Enum)

What is the DCL?

Double checked locking.

public class Singleton {
    private volatile static Singleton uniqueSingleton;
    private Singleton(a) {}public Singleton getInstance(a) {
        if (null == uniqueSingleton) {
            synchronized (Singleton.class) {
                if (null == uniqueSingleton) {
                    uniqueSingleton = newSingleton(); }}}returnuniqueSingleton; }}Copy the code

What is the unsafe class?

Unsafe is a low-level class in Java. Contains many basic operations, such as array operations, object operations, memory operations,CAS operations, threads (park) operations, fence operations. The JUC package and some third-party frameworks use Unsafe for concurrency security. This class is a class in the Sun.*API, and it’s not really part of J2SE, so there’s no official documentation, no code documentation. The Unsafe class design is intended only for JVM trusted startup classloaders and is a typical singleton pattern class. Calling the safe.getunSafe () method directly from a non-startup class loader throws a SecurityException.

  1. Memory management, including allocating memory, freeing memory, etc
  2. Unconventional object instantiation. useallocateInstance()Method can generate object instances directly without calling constructors and other initialization methods.
  3. Manipulate classes, objects, variables.
  4. Array operating. including the offset address of the first element of the array, the incremental address of the element in the array.
  5. Multi-threaded synchronization, including locking mechanism,CAS operation, etc.
  6. Suspend and resume, including park,unpark, and other methods.
  7. This section includes methods such as loadFence,storeFence, etc. Talk about Java’s Unsafe class
public static Unsafe getUnsafe(a) {
 Class cc = sun.reflect.Reflection.getCallerClass(2);
 if(cc.getClassLoader() ! =null)
  throw new SecurityException("Unsafe");
 return theUnsafe;
}
Copy the code

What is a CAS?

Compare and Swap. It’s a lock-free algorithm.

How does compareAndSet work?

Compare the current working memory value to the main memory value, if the two values are equal, no other thread has changed the main memory value, then perform the operation, if not continue to loop.

What are the disadvantages of CAS?

  1. The loop is time-consuming and CPU expensive
  2. Only one shared variable is guaranteed atomicity at a time
  3. ABA problem

What are ABA problems?

The value of A variable changes from A to B, and then from B to A. Problems may occur in actual scenarios. For example, if you add an element to the stack, if the top of the stack is A, you add an element to the stack, it’s possible that the element on the stack has changed but the top of the stack is still A.

How to solve the ABA problem?

Not only are values in working and main memory compared, but their version numbers are also compared. You can use the AtomicStampedReference class. Code examples:

public class CASDemo {
    private static AtomicStampedReference<Long> stampedReference = new AtomicStampedReference<>(20L.1);
    public static void main(String[] args) {
        new Thread(()->{
            int stamp = stampedReference.getStamp();// Get the version number
            System.out.println("B:"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);// Sleep 2 seconds
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("B:"+stampedReference.compareAndSet(20L.30L, stamp, stampedReference.getStamp() + 1));
            System.out.println("B:"+stampedReference.getStamp());
        },"B:").start();
        new Thread(()->{
            int stamp = stampedReference.getStamp();// Get the version number
            System.out.println("A:"+stamp);
            try {
                TimeUnit.SECONDS.sleep(1);// Sleep for 1 second
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("A:"+stampedReference.compareAndSet(20L.50L, stampedReference.getStamp(), stampedReference.getStamp() + 1));
            System.out.println("A:"+stampedReference.getStamp());
            System.out.println("A:"+stampedReference.compareAndSet(50L.20L, stampedReference.getStamp(), stampedReference.getStamp() + 1));
            System.out.println("A:"+stampedReference.getStamp());
        },"A:").start(); }}Copy the code

Execution Result:

B:1
A:1
A:true
A:2
A:true
A:3
B:false
B:3
Copy the code

Summary: After you use your AtomicStampedReference, the value is the same, the version is changed, and you still cannot execute set. Whether to use AtomicStampedReference depends on your business scenario. Many times, if the value or version changes and you need to retry, you need to wrap the atomic operation of the variable in a while loop for retry.

What is the pit in the Integer wrapper class?

Between -128 and 127, you can use == to check, and beyond this range you should use equals to check.

What is a fair lock?

Lock and unlock operations are performed in a first-come-first-served order.

What is an unfair lock?

Efficiency first, lock process can jump the queue.

What is a reentrant lock?

Take the outside lock and you automatically get the inside lock. Code examples:

public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            try {
                phone.call();
            } catch(InterruptedException e) { e.printStackTrace(); }},"A:").start();
        new Thread(()->{
            try {
                phone.sendSms();
            } catch(Exception e) { e.printStackTrace(); }},"B:").start(); }}class Phone{
    public synchronized void call(a) throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+" call");
        TimeUnit.SECONDS.sleep(2);// Sleep 2 seconds
        sendSms();// Automatically get the sendSms lock
    }
    public synchronized void sendSms(a){
        System.out.println(Thread.currentThread().getName()+" sendSms"); }}Copy the code

Execution Result:

A: call
A: sendSms
B: sendSms
Copy the code

Do locks have to be paired?

Must be paired.

What is a spin lock?

When a thread acquires a lock, if the lock has already been acquired by another thread, the thread will loop and then continuously check whether the lock has been acquired successfully until it exits the loop. Spinlocks reduce the time it takes for threads to switch contexts, but they can be a waste of CPU if the lock waits longer.

How to implement spin lock?

Set the variable value to currentThread when locking and null when unlocking. CAS is used for locking and unlocking.

/ / the spin lock
public class SpinLockDemo {
    private AtomicReference<Thread> reference = new AtomicReference<>(null);
    / / lock
    public void lock(a){
        // If the thread is not empty, keep spinning
        while(! reference.compareAndSet(null,Thread.currentThread())){
        };
    }
    / / unlock
    public void unlock(a){
        // If the current thread has been locked, the locked thread is null
        reference.compareAndSet(Thread.currentThread(),null); }}Copy the code

However, the above method does not achieve reentrant lock. Reentrant can be achieved by adding a counter.

public class ReentrantSpinLock {
    private AtomicReference cas = new AtomicReference();
    private int count;
    public void lock(a) {/ / lock
        Thread current = Thread.currentThread();
        if (current == cas.get()) { // If the current thread has acquired the lock, the number of threads increases by one, and then returns
            count++;
            return;
        }
        // If no lock is obtained, spin through CAS
        while(! cas.compareAndSet(null, current)) {
            // DO nothing}}public void unlock(a) {/ / unlock
        Thread cur = Thread.currentThread();
        if (cur == cas.get()) {
            if (count > 0) {// If the value is greater than 0, the current thread has acquired the lock multiple times. Releasing the lock is simulated by counting minus one
                count--;
            } else {// If count==0, the lock can be released, so that the number of times the lock is acquired is the same as the number of times the lock is released.
                cas.compareAndSet(cur, null); }}}}Copy the code

Conclusion: Reentrant locking can be achieved using a counter. The underlying implementation of ReentrantLock is the same: when acquiring the lock, if state is 0,state+1, set the thread acquiring the lock to the current thread. Otherwise, tryAcquire() is executed and the lock is acquired again. If not,state+1. If it is the current thread,state+1. Otherwise, the lock fails.

How does the lock work? Is it defined inside the method or outside the method?

A lock also locks objects. If you place it in a method, each time you fetch a different object, you cannot lock it effectively. So you have to define it outside of the method.

What is a deadlock?

Two threads want to acquire each other’s lock while holding the lock.

public class DeadLockDemo {
    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(a) {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"Hold"+lockA+"To get"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println("> > > > > > > > > > > > > > > > > > > > > > > > > > >"); }}}}Copy the code
T1 holds lockA and wants to getlockB T2 holds lockB and wants to getlockACopy the code

How to use the tool to troubleshoot deadlocks?

  1. usejps -lCommand to locate the process ID
  2. useJstack process,Find the deadlock.