java multithreading - JUC

Posted by celebx on Thu, 30 Dec 2021 06:42:39 +0100

java high concurrency multithreading learning of JUC

1. Definition of JUC

Source code + official documents

java. Three interfaces and tool classes under util Toolkit

**Business: * * common Thread code: Thread

**Runnable interface: * * no return value, lower efficiency than Callable

2. Threads and processes

Threads and processes are summarized in one sentence

**Process: * * a program, such as QQ exe,Music. Collection of exe programs

  • A process can often contain multiple threads, at least one!
  • java has two threads by default: main and GC

**Thread: * * for example, a Typora process is currently started, in which a specific thread is responsible for single operations such as typing, saving and deleting

  • java implements threads: Thread, Runnable, Callable.

Can Java programs really start threads? Look at the source code!

/**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
public synchronized void start() {
    /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
        }
    }
}

private native void start0();
  • Note that the start0() method is modified with the native keyword. This method is a local method, not completed by java programs, but operated by other C/C + + programs. Because java programs must be executed in the JVM environment and cannot directly operate computer hardware, the creation of threads is not completed by java programs in essence!

The difference between concurrency and parallelism

Concurrent programming: concurrent, parallel

Concurrency: (multiple threads operate on the same resource)

  • One CPU core simulates multiple threads to achieve concurrency through fast alternate execution

Parallel: (multiple threads execute simultaneously)

  • The CPU is multi-core, and multiple threads can be executed at the same time; Thread pool.

The essence of concurrent programming: make full use of CPU resources!

Several states of threads

/**
     * A thread state.  A thread can be in one of the following states:
     * <ul>
     * <li>{@link #NEW}<br>
     *     A thread that has not yet started is in this state.
     *     </li>
     * <li>{@link #RUNNABLE}<br>
     *     A thread executing in the Java virtual machine is in this state.
     *     </li>
     * <li>{@link #BLOCKED}<br>
     *     A thread that is blocked waiting for a monitor lock
     *     is in this state.
     *     </li>
     * <li>{@link #WAITING}<br>
     *     A thread that is waiting indefinitely for another thread to
     *     perform a particular action is in this state.
     *     </li>
     * <li>{@link #TIMED_WAITING}<br>
     *     A thread that is waiting for another thread to perform an action
     *     for up to a specified waiting time is in this state.
     *     </li>
     * <li>{@link #TERMINATED}<br>
     *     A thread that has exited is in this state.
     *     </li>
     * </ul>
     *
     * <p>
     * A thread can be in only one state at a given point in time.
     * These states are virtual machine states which do not reflect
     * any operating system thread states.
     *
     * @since   1.5
     * @see #getState
     */
    public enum State {
        // establish
        NEW,
		// function
        RUNNABLE,
		// block
        BLOCKED,
		// Wait - wait all the time
        WAITING,
		// Timeout wait - limit time (out of date)
        TIMED_WAITING,
		// Destroy
        TERMINATED;
    }

The difference between wait/sleep

1. From different classes

Wait() = > > > object class sleep() = = > > > thread class

2. Release of lock

wait will release the lock. sleep if someone falls asleep and forgets to release the lock!

3. The scope of use is different

Wait = = > > > sync code block sleep = = > > > anywhere

4. Exception capture

Both require interrupt exception capture!

3. Lock lock (key)

Traditional synchronized lock

Lock lock

Fair lock: very fair, first come, first served

Unfair lock: very unfair: you can jump the queue (default)

The difference between Synchronized Lock and Lock lock

1. Synchronized is a built-in Java keyword, and Lock is a java interface;

2. Synchronized cannot judge the status of obtaining the lock. Lock can judge whether the lock has been obtained;

3. Synchronized will automatically release the Lock. The Lock must be released manually. If the Lock is not released, a deadlock will occur;

4. Synchronized threads (they will enter the blocking state when acquiring the lock), and other threads can only wait for the current thread to release the lock; Lock can try to obtain the current lock, and may not wait all the time;

5. Synchronized reentrant lock, non interruptible, non fair lock; Lock is a reentrant lock. You can judge the status of the lock. It is a non fair lock by default, but you can set it yourself;

6. Synchronized is suitable for locking a small number of code synchronization problems, and Lock is suitable for locking a large number of synchronized code blocks.

What is a lock? How to determine who is the object of the lock?

4. Producer and consumer issues

Interview procedures: single case mode, eight sorting algorithms, producers and consumers, deadlock

Synchronized wait notify is an old version!

The problem exists. There is no problem with the execution of threads a and B; A. If B, C and D threads are started at the same time, can thread synchronization be guaranteed? - spurious wakeup

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-e4Zzee9r-1640832881517)(D:\APP document \ learning notes \ JAVA.assets\image-20210323210708371.png)]

  • Solution: change if word judgment to circular judgment

For the producer and consumer issues of the JUC version, please refer to the JDK source code:

class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock(); try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally { lock.unlock(); }
   }

   public Object take() throws InterruptedException {
     lock.lock(); try {
       while (count == 0)
         notEmpty.await();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally { lock.unlock(); }
   }
 } 
  • Synchronized version: wait() method | notify() method
  • Lock version: await() method | signal() method

What are the advantages of the Condition interface Precise notification and wake-up threads

5.8 lock phenomenon - solve the problem of judging the object of the lock

Objects – > multiple / class – > unique

  • 8 Lock problem 1--->>Under standard conditions, whether the two threads send text messages first or call first(Send text messages first, then make phone calls)
    
  • Eight lock problem 2--->>With the addition of asynchronous methods, thread execution is not affected by locks(Execute asynchronous method first);
    * Two objects and two synchronous methods. Whoever executes first will have a lock to lock the method caller. Those without delay will be executed first
    
  • Eight lock problem 3--->>Two static synchronization methods are added. The static method is generated when the class is loaded.class Template. All static methods in a class use the same template
    * Class Template, so static Methods are executed sequentially, regardless of how many objects make method calls
    
  • Eight lock problem 4--->>A common synchronization method and a static synchronization method, when an object is called;
    * A common synchronization method and a static synchronization method, when two objects are called; My method is to execute static synchronization first
    

Summary

  • this keyword can be modified for the instance of new, which is a concrete instance of a class;

  • static Class template is the first mock exam of a class.

6. The collection class is unsafe

List is not safe

  • Concurrent modificationexception will be caused!

The solution is as follows:

1,List<String> list = new Vector<>();
2,List<String> list = Collections.synchronizedList(new ArrayList<>());
3,List<String> list = new CopyOnWriteArrayList<>(); because Vector The read and write methods in are synchronous methods, which are not as efficient as those in execution CopyOnWriteArrayList The asynchronous method is efficient, CopyOnWriteArrayList Used in Lock Lock. It is flexible to operate the lock status

Set is not safe

The solution is as follows:

1,Set<String> set = Collections.synchronizedSet(new HashSet<>()); Ask dad to help
2,Set<String> set = new CopyOnWriteArraySet<>();
  • What is the underlying hashSet?
/**
  * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
  * default initial capacity (16) and load factor (0.75).
 **/
public HashSet() {
    map = new HashMap<>();
}
/**
  * add The underlying essence of method set is map, and the key in hashmap cannot be repeated
 **/
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

Map is not secure

7. Callable

Similarities and differences between Callable and Runnable

  • Can have return value;

  • Exceptions can be thrown;

  • The implementation methods are different: run()/call()

  • Details:

    • 1. Cache to improve efficiency;
    • 2. The result may need to wait and block!

8. Common auxiliary classes

8.1 CountDownLatch

Principle:

  • countDownLatch.countDown();	// Initialization quantity minus 1
    
  • countDownLatch.await();	// Wait for the counter to return to zero, and then execute the following program (for interception). Each time, use the countdown() method to reduce the counter value by 1. When the counter value is 0, the await() method will be awakened and continue to execute the subsequent program!
    

8.2 CyclicBarrier

for (int i = 1; i <= 7; i++) {
    final int temp = i;
    // lambda expressions cannot be manipulated directly i
    new Thread(()-> {
        System.out.println(Thread.currentThread().getName() + "Collected" + temp + "A dragon ball");
        try {
            // wait for
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }).start();
}

8.3 Semaphore

Principle:

  • semaphore.acquire();	// Obtain resources. If all resources are used, wait for the resources to be released -- semaphore-1 operation
    
  • semaphore.release();	// Release resources, - semaphore + 1 operation, waiting for other threads to schedule
    

effect:

Use of mutual exclusion of multiple shared resources, concurrent flow restriction, and control the maximum number of threads

9. Read write lock

**ReadWriteLock**

Custom cache---ReadWriteLock(Exclusive lock-Write lock[It can only be occupied by one thread at a time];Shared lock-Read lock[It can be occupied by multiple threads at a time])
1,read-Reading: can coexist;
2,read-Write: cannot coexist;
3,write-Write: cannot coexist

Example:

class MyCacheLock {
    private volatile Map<String, Object> map = new HashMap<>();
    /**
     * Read / write lock, more fine-grained control
     **/
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * Save / write operation
     */
    public void put(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "write in" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "write in ok");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    /**
     * Fetch / read operation
     */
    public void get(String key) {
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "read" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "read ok");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

10. Blocking queue

Blocking / queue: [write operation] - if the queue is full, you must block and wait for the read operation; [read operation] - if the queue is empty, it must be blocked waiting for write operation.

  • realization:

When will blocking queues be used? - Multithreaded concurrent execution / thread pool using thread queue

  • Learn to use queues

Add / remove

Four sets of API s

modeThrow exceptionIf there is a return value, no exception will be thrownBlocking waitTimeout wait
add toadd()offer()put()offer(E e, long timeout, TimeUnit unit)
removeremove()poll()take()poll(long timeout, TimeUnit unit) throws InterruptedException
Detection and judgment queue headerelement()peek()

Synchronous queue

There is no capacity. If you enter an element, you must wait for it to be taken out before you can put the element again

  • Synchronization queue-And others BlockQueue Different, SynchronousQueue Do not store elements to the queue put An element must be used first from the inside take Take it out by operation, otherwise it can't be taken out again put Other elements
    

11. Thread pool (key)

[three methods, seven parameters and four rejection strategies]

**Pooling Technology: * * prepare some resources in advance. If someone wants to use them, read these ready-made resources and return them after use!

  • The essence of program operation: it occupies system resources, so pooling technology = = > optimize the use of resources!

Thread pool, connection pool, memory pool, object pool

  • Advantages of thread pool:

1. Reduce resource consumption;

2. Improve response speed;

3. Easy to manage.

1. Three methods of thread pool:

// Single thread
Executors.newSingleThreadExecutor();
// Create a fixed size thread pool
Executors.newFixedThreadPool(5);
// Scalable thread pool, strong in case of strength and weak in case of weakness
Executors.newCachedThreadPool();

2. Seven parameters of thread pool:

Source code analysis:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,				// The more is equal to 2.1 billion: it is easy to lead to OOM
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

// Essence: ThreadPoolExecutor() to create a thread pool
public ThreadPoolExecutor(int corePoolSize,						// Core thread pool size
                          int maximumPoolSize,					// Maximum thread pool size
                          long keepAliveTime,					// Thread lifetime: it will be released if no one calls it after timeout
                          TimeUnit unit,						// Timeout unit
                          BlockingQueue<Runnable> workQueue,	// Blocking queue
                          ThreadFactory threadFactory,			// Thread factory: creates a thread, which generally does not move
                          RejectedExecutionHandler handler) {	// Reject policy
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
        null :
    AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

Manually create a thread pool:

ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                // When the queue is full, it will try to compete with the oldest thread and will not throw an exception
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );

Four rejection strategies:

{new ThreadPoolExecutor.AbortPolicy());}-->If the blocking queue is full and there is thread access, this thread will not be accepted and an exception will be thrown
{new ThreadPoolExecutor.CallerRunsPolicy());}-->Go back where you come from!
{new ThreadPoolExecutor.DiscardPolicy());}-->When the queue is full, the remaining threads will be lost and no exceptions will be thrown
{new ThreadPoolExecutor.DiscardOldestPolicy());}-->When the queue is full, it tries to compete with the oldest thread,No exceptions will be thrown

Summary and extension:

  • Understanding: io intensive / cpu intensive

How to set the maximum number of threads in the thread pool:

Custom thread pool: how to define the maximum thread:
1,cpu Intensive: computer cpu For several cores, the maximum thread pool is defined as how large it is, so that it can be maintained cpu Is the most efficient
2,IO Intensive: it is very expensive in the judgment program IO Thread(15 A large mission, io Very resource intensive)

12. Four function interfaces (must be mastered)

  • Programmers in the new era: lambda expression, chain programming, functional interface, Stream flow computing.

**Functional interface: * * interface with only one method

  • Example:
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

// @Functional interface: simplifies the programming mode and is widely used in the new version of the framework
// Foreach (the parameter is the functional interface of the consumer class)

Function: functional interface

// Tool class: output input value
Function<String, String> function = str -> str;

Predicate: predicate interface

// Decision interface: there is an input parameter, and its return value can only be boolean value
Predicate<String> predicate = String::isEmpty;

Consumer: consumer interface

// Consumer interface: only input, no output
Consumer<String> consumer = System.out::println;

Supplier: supply type interface

// Supply type interface: no parameters, only return values
Supplier<Integer> supplier = () -> 1024;

13. Stream flow calculation

What is Stream streaming?

  • Big data: storage + calculation - > collection. The database is used to store things. All calculations should be handled by the flow!
list.stream()
                .filter(user -> user.getId() % 2 == 0)
                .filter(user -> user.getAge() > 23)
                .map(user -> user.getName().toUpperCase())
                .sorted(Comparator.reverseOrder())
                .limit(1)
                .forEach(System.out::println);

14. ForkJoin

What is ForkJoin?

ForkJoin is in jdk1 7 appears and executes tasks in parallel! increase of efficiency. Large amount of data!

  • Big data: Map Reduce

  • ForkJoin features: work stealing - improve thread efficiency

How to use ForkJoin:

  • 1. Call through the ForkJoinPool interface:

  • 2. Compute task forkjointask Execute (functional interface)

15. Asynchronous callback

Original intention of Future design: to model a Future result

// Send a request - asynchronous callback with no return value
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(4);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
});

System.out.println("1111");

completableFuture.get();    // Get blocking execution results


// Asynchronous callback with return value - ajax contains success / failure callback functions
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName() + "runAsync=>Integer");
    int i = 10/0;
    return 1024;
});

completableFuture.whenComplete((t, u) -> {
    System.out.println("t => " + t);    // Normal return result
    System.out.println("u => " + u);
}).exceptionally(e -> {
    System.out.println(e.getMessage());
    return 233;
});

16. JVM

Understand the Volatile keyword!

Volatile is a lightweight synchronization mechanism provided by java virtual machine!

  • 1. Ensure visibility;
  • 2. Atomicity is not guaranteed;
  • 3. Prohibit instruction rearrangement

What is JMM?

**JMM:**java memory model is an abstract concept or convention!

  • 1. Before the thread is unlocked, the shared variable = = must be flushed back to main memory immediately;
  • 2. Before locking the thread, the latest stored value must be read into the working memory;
  • 3. Locking and unlocking must be the same lock.

8 operations:

Eight operations are defined in the Java memory model, and the virtual machine ensures that each operation is atomic.

  • lock: a variable that acts on main memory and identifies a variable as a thread exclusive state.
  • unlock: it acts on the main memory variable to release a locked variable, and the released variable can be locked by other threads.
  • read: acts on main memory variables and transfers the value of a variable from main memory to working memory.
  • load: it acts on the working memory variable and puts the value from read into the variable copy of working memory.
  • use: it acts on the working memory variable and passes the value of a variable in the working memory to the execution engine. This operation is executed whenever the virtual machine encounters a bytecode instruction assigned to the variable.
  • store: acts on working memory variables and transfers the value of a variable in working memory to main memory.
  • write: it acts on the main memory variable and puts the value of the variable obtained from the working memory by the store operation into the main memory variable.

If you want to copy a variable from main memory to working memory: perform read and load operations in sequence.

If you want to synchronize variables from working memory, the main memory: store and write operations are performed sequentially.

17. Volatile

1. Ensure visibility

private static volatile int num = 0;	// volatile keyword decoration ensures visibility

2. Atomicity is not guaranteed – indivisible (threads cannot be disturbed or split when executing tasks. They either succeed or fail at the same time!)

// Theoretically 20x1000=20000
for (int i = 1; i <= 20; i++) {
    new Thread(()-> {
        for (int j = 0; j < 1000; j++) {
            add();
        }
    }).start();
}

while (Thread.activeCount() > 2) {
    Thread.yield();
}

System.out.println(Thread.currentThread().getName() + " " + num);

// Execution result: main 15595 (less than 20000)

If Lock and Synchronized locks are not applicable, if atomicity is guaranteed?

  • The operations of these classes are directly linked to the operating system to modify the value in memory! Unsafe class is a very special class!

What is instruction rearrangement?

  • When the program is executed, the computer does not execute in the order you write.

Source code - compiler optimized rearrangement - instruction parallelism may also be replayed - memory system will also be replayed - Execution

  • When the processor rearranges instructions, it needs to consider the dependence between data
int x = 1;	// 1
int y = 2;	// 2
x = x + 5;	// 3
y = x * x;	// 4

// Desired operation 1234, but 2134 / 1324 can achieve the same effect
// It can't be 4123
Thread AThread B
x = ay = b
b = 1a = 2

Normal results: x = 0; y = 0. However, the order in the following table may also occur due to instruction rearrangement!

Thread AThread B
b = 1a = 2
x = ay = b

Weird result caused by instruction reordering: x = 2; y = 1.

volatile can avoid instruction rearrangement!

Memory barrier / CPU instruction function:

  • 1. Ensure the normal sequence of specific operations;
  • 2. Memory visibility of some variables can be guaranteed.

18. Singleton mode

Hungry man style DCL lazy man style, deep study!

package com.juc.singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * @Description: Lazy singleton mode
 * @author: L-B-S
 * @Date: 2021/3/26 18:37
 * @modified_By:
 * @Version: 0.0$
 */
public class Lazy {

    private static volatile Lazy lazy;

    private static boolean flag = false;

    private Lazy() {
        synchronized (Lazy.class) {
            if (!flag) {
                flag = true;
            } else {
                throw new RuntimeException("Do not attempt to break exceptions with reflection");
            }
        }
    }

    public static Lazy getInstance() {
        // Lock
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    // Not an atomic operation: 1. Allocate memory space; 2. Execute the construction method and initialize the object; 3. Point this object to memory space
                    lazy = new Lazy();
                }
            }
        }
        return lazy;
    }
    // Safety under single thread

    public static void main(String[] args) throws Exception {
//        Lazy instance1 = Lazy.getInstance();

        Field flag = Lazy.class.getDeclaredField("flag");
        flag.setAccessible(true);

        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Lazy instance1 = declaredConstructor.newInstance();

        flag.set(instance1, false);

        Lazy instance2 = declaredConstructor.newInstance();

        System.out.println(instance1 == instance2);
    }
}

19. Deeply understand CAS - cultivate internal skills

What is CAS?

  • Non spin lock used at the bottom of CAS: when comparing the value in the current working memory with the value in main memory, if this value is the expected value, subsequent operations will be performed; Otherwise, it will wait in a cycle!
  • Disadvantages:
  • 1. The cycle will take time;
  • 2. One time can only guarantee the atomicity of one shared variable;
  • 3. It can easily lead to ABA problems.

CAS core issue: ABA issue (for short: civet cat for Prince)

// Troublemaker thread
AtomicInteger atomicInteger = new AtomicInteger(2020);
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());

// Expected thread
System.out.println(atomicInteger.compareAndSet(2020, 6666));
System.out.println(atomicInteger.get());

20. Atomic reference

Solution to ABA problem: atomic reference is introduced to solve it perfectly. There will be version records for each change!

package com.juc.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @Description: CAS---CPU Concurrent primitives!
 * @author: L-B-S
 * @Date: 2021/3/26 20:17
 * @modified_By:
 * @Version: 0.0$
 */
public class CASDemo {

    public static void main(String[] args) {

        // int Integer note: if the generic is a wrapper class, pay attention to the reference of objects during comparison
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);

        new Thread(() -> {
            // Get version number
            int stamp = atomicStampedReference.getStamp();
            System.out.println("a1 => " + stamp);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(atomicStampedReference.compareAndSet(1, 2,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));

            System.out.println("a2 => " + atomicStampedReference.getStamp());

            System.out.println(atomicStampedReference.compareAndSet(2, 1,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));

            System.out.println("a3 => " + atomicStampedReference.getStamp());

        }, "A").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println("b1 => " + stamp);

            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(atomicStampedReference.compareAndSet(1, 3,
                    stamp, stamp + 1));
        }, "B").start();

        System.out.println("b2 => " + atomicStampedReference.getStamp());


    }
}

21. Understanding of various locks

1. Fair lock

Fair lock: very fair (no jumping in line)

Unfair lock: very unfair (you can jump in the queue, which is unfair by default)

/**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
public ReentrantLock() {
    sync = new NonfairSync();
}

Lock lock = new ReentrantLock(true);	// Overload method: pass in true, which is a fair lock, and false, which is a non fair lock

2. Reentrant lock / recursive lock

The Synchronized keyword implements a reentrant lock

class Phone1 {
    public synchronized void sendMsg() {
        System.out.println(Thread.currentThread().getName() + " => sendMsg");
        call();
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + " => call");
    }
}

Lock interface implements reentrant lock

class Phone2 {
    Lock lock = new ReentrantLock();

    public synchronized void sendMsg() {
        // The first lock -- the lock must be paired, and the lock must be unlocked several times after adding the lock
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " => sendMsg");
            // Lock inside call method reached
            call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public synchronized void call() {
        // Second lock
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " => call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
  • Details: attention should be paid to the pairing of locks - that is, if several locks are used, there must be several lock keys (unlocking), otherwise deadlock will occur

3. Spin lock

SpinLock:

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;
}
  • CAS bottom layer: standard spin lock!

4. Deadlock

What is a deadlock?

  • How to troubleshoot / test deadlocks:

Resolve Deadlock:

1. Use jps -l to locate the process number;

D:\java_study\juc-study>jps -l
9936 com.juc.lock.DeadLockDemo
12804 sun.tools.jps.Jps
17828 org.jetbrains.jps.cmdline.Launcher
12872
14024 org.jetbrains.idea.maven.server.RemoteMavenServer36

2. Use the jstack process number to find the deadlock problem

   e.printStackTrace();
    } finally {
        lock.unlock();
    }
}

public synchronized void call() {
    // Second lock
    lock.lock();
    try {
        System.out.println(Thread.currentThread().getName() + " => call");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}

}

- `details`: **Pay attention to the pairing of locks->That is, if several locks are used, there must be several lock keys(Unlock),Otherwise, it will cause deadlock**

#### 3. Spin lock

> **SpinLock: **

```java
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;
}
  • CAS bottom layer: standard spin lock!

4. Deadlock

What is a deadlock?

[external chain picture transferring... (img-A9umsD87-1640832881526)].

  • How to troubleshoot / test deadlocks:

Resolve Deadlock:

1. Use jps -l to locate the process number;

D:\java_study\juc-study>jps -l
9936 com.juc.lock.DeadLockDemo
12804 sun.tools.jps.Jps
17828 org.jetbrains.jps.cmdline.Launcher
12872
14024 org.jetbrains.idea.maven.server.RemoteMavenServer36

2. Use the jstack process number to find the deadlock problem

Topics: Java Back-end