Basic knowledge of JUC (personal summary)

Posted by adrafa on Sat, 05 Mar 2022 02:19:50 +0100

Statement: 1 ***
              2. Because it is a personal summary, write the article with the most concise words
              3. If there is any mistake or improper place, please point out

I Front Foundation:

  • The IO operation does not occupy cpu, but we generally copy files using [blocking IO]. At this time, it is equivalent to that the thread does not use cpu, but needs one

    Waiting for the end of IO failed to make full use of threads.

    Therefore, there are the following [non blocking IO] and [asynchronous IO] optimization

  • synchronized synchronization is mutually exclusive

  • The essence of a lock is that everyone has mutually exclusive access to a shared resource

  • The stack is thread private and the heap is shared by multiple threads;

    However, it does not mean that the local variables in the thread stack are accessed by other threads, and escape analysis needs to be carried out first;

    For example, if the local variable is passed to the thread opened inside the method, the variable will escape from the method, and there will be thread safety problems

  • Process is the encapsulation of runtime program and the basic unit of resource scheduling and resource allocation of operating system

    Thread is the basic unit of cpu scheduling

    The tube side is the monitor, i.e. synchronized lock

    For example, a running Java program is a JVM process, in which there are both user threads and GC garbage collection threads (daemon threads); It can be seen that there can be multiple threads in a process

  • Whether it is the main thread or the custom thread, only the surviving JVM will not end;

    When there are only daemon threads, the JVM ends

  • The lock granularity should not be too large, but not too small; Both concurrency and security should be considered

Method of thread viewing

Windows

tasklist view process

taskkill kill process

Linux

PS - TF - P < PID > view all threads of a process (PID)

Top - H - P < PID > view all threads of a process (PID)

Java

Jstack < PID > view all thread states of a Java process (PID)

Stack frame:

Each stack consists of multiple stack frames, corresponding to the memory occupied by each method call

Each thread can only have one active stack frame, corresponding to the method currently executing

The stack frame includes: local variable, return address, operand stack, args parameter and lock record

Multithreading is to make full use of cpu resources and will not block the whole business process because of a single block.

Advantages of multithreaded serial over single thread:

Multi thread serial, even if one thread is blocked, it will not affect other threads;

Moreover, when the critical area is not modified and the race condition occurs, there is no need to lock, and the advantage is more obvious

Synchronization / asynchrony is viewed from the business logic relationship between multiple threads or callback notification mechanism

Serial / parallel is from the perspective of the actual execution process between multiple threads

Asynchronous business: Commodity detail business & comment business
Synchronization business: login function business & shopping cart function business

Generally speaking, because the business is asynchronous, it leads to parallel execution; Because the service is synchronous, it leads to serial execution

Synchronization:

  1. Two things maintain some relative relationship
  2. syncronized mutex
  3. There is no callback on the message notification mechanism
  4. Business synchronization, with execution sequence

join(): queue jumping, that is, waiting for this thread to end

join(3000L) can jump the queue for 3s at most. When it times out, main won't wait for t1. If t1 goes, main continues to go down

// t1 takes 1s, t2 takes 2s, and the total cost is not 3s, but 2s; t1 and t2 can be regarded as running in parallel
public static main(String[] args){
    Thread t1 = new Thread(( ) -> {
        sleep(1);
        r1 = 10;
    });
    Thread t2 = new Thread(( ) -> {
        sleep(2);
        r2 = 20;
    });
    t1.start( );
    t2.start( );
    t1.join( );	//It is called by the main thread, so jump the queue before main
    t2.join( );
}		

yield(): give way

LockSupport.park( ) LockSupport.unpark( );

You can call unpark first, and the next park will be invalid; Note that the unpark called first can only be accumulated once and cannot be superimposed

Thread status at the operating system level:

Thread state at JVM level:

The RUNNABLE state at the Java level covers the [RUNNABLE state], [running state] and [blocking state] at the operating system level (thread blocking caused by BIO can not be distinguished in Java and is still considered RUNNABLE)

The blocking states at the Java level include: blocked, waiting and timed_ Waiting (wait with time)

Several ways of thread communication:

  • Shared memory (global variable)
  • synchronized lock
  • wait( )/notify( )
  • join( )
  • Concurrent tools: CountDownLatch, CyclicBarrier
  • The Conduit
  • Semaphore

II synchronized bottom layer:

The bottom layer of synchronized is Monitor, which is translated into Monitor, heavyweight lock or tube pass

Monitor infrastructure:

  1. Owner the thread that snatched the lock
  2. EntryList thread blocked due to lock failure
  3. The thread of WaitSet wait is equivalent to a lounge; So wait must be executed in synchronized

synchronized bytecode:

The astore in line 4 is the reference to store the lock, because the lock needs to be found and released later

There are two monitorexits after the monitorenter. The first monitorexit is to release the lock during normal execution, and the second monitorexit is to release the lock when an exception occurs

The lock related information (thread id, whether it is a biased lock, and the number of lock reentry) is stored in MarkWord in the object header

JDK6 introduces the bias lock & lightweight lock, and has the concept of lock upgrade

1. Deflection lock:

When there is no competition (just one thread), the lightweight lock still needs to perform CAS operation every time it re enters

Bias lock: only when CAS is used for the first time, the thread ID is set to the Mark Word header of the object, and then it is found that the thread ID is its own, which means there is no competition and there is no need to re CAS

Biased locks can be redirected to other threads

Undo deflection lock:

That is, upgrading to a lightweight lock requires STW

If the undo bias reaches a certain threshold, all objects of the entire class become unbiased

2. Lightweight lock:

Multiple threads stagger access, and lightweight locks can be used to optimize when there is no competition

3. Heavyweight lock:

There is competition between multiple threads

The state of the synchronized lock can only be upgraded and cannot be degraded

Other lock concepts:

Spin lock: don't rush to block after the lock grabbing failure, but try several times in multiple cycles; Because there is a price for threads to switch between blocking state and runnable state

It is implemented using CAS

Lock inflation: that is, lock upgrade, bias lock - > lightweight lock - > heavyweight lock

Lock coarsening: Several synchronized lock objects are coarsened into a synchronized code block without other code in the middle

Lock elimination: after escape analysis, if it is found that the local variable does not escape from this method, the lock added to this local variable will be eliminated

Deadlock: two threads lock each other's lock, and are unwilling to release their own lock first, resulting in a deadlock of waiting for each other

Generally, a synchronized code block is nested in a synchronized code block

Conditions for deadlock occurrence:

   1. Hold each other's locks, And they are unwilling to release the existing lock first
   2. Lock for each other, Non obtainable
   3. No external interference interrupt

Detailed version:

  1. Mutually exclusive condition: the process is exclusive to the allocated resources, that is, a resource can only be occupied by one process until it is released by the process
  2. Request and hold condition: when a process is blocked due to the resources occupied by the request, it will hold the obtained resources
  3. No deprivation condition: no other process can deprive and occupy any resource before it is released by the process
  4. Loop waiting condition: when a deadlock occurs, the waiting process must form a loop (similar to an endless loop), causing permanent blocking.

Solution:

   1. Active abandonment of lock:  use if+tryLock, It is not mandatory to acquire a lock, If you can get it, you can get it back true Then execute,Can't get back false Even if it's done, it won't be implemented
   2. Passive abandonment lock:  Interruption of external intervention, call interrupt method  

Livelock: two threads perform opposite operations. For example, thread A performs + + operation on cnt with an initial value of 10, and thread B performs - operation on cnt;

The two threads have been running, but the value of cnt can't reach 0 for a long time and can't finish running

Fair lock: all threads are covered with rain and dew to prevent some threads from being too strong and finishing all work, resulting in other threads starving and not working;

However, this will reduce concurrency, so it is not recommended;

‚Äč new ReentrantLock(true); It is a fair lock, and the default is false

Reentrant lock: obtain the same lock repeatedly inside the lock code block, also known as recursive lock

III Multithreading security issues:

Multithreading safety problem: multiple threads write to a shared memory, that is, a race condition occurs in the critical area

If there are multithreaded read-write operations on shared resources in a code block, this code block is called a critical area

When multiple threads execute in the critical area, the results cannot be predicted due to different execution sequences of codes, which is called race condition

False wake-up: when the thread in if is interrupted by wait and wakes up again, the data may have changed, but the judgment is not repeated

1. Atomicity:

  • Atomicity of i + +: at first CNT = 0, thread A + + gets 1, but the pending write is blocked, and B writes to 0-cheng-1. At this time, A continues to write to 1; Caused 0 + + -- = 1

  • Atomicity among multiple operations: thread switching leads to inaccurate if judgment. For example, if (10000 > 8000), if (10000 > 4000) and if (10000 > 2000) go shopping for 10000 yuan. Because the thread is interrupted, there is no time to deduct, and finally 10000-8000-4000-2000 < 0 debt

Solution:

  1. Blocking solution: synchronized, AQS

  2. Non blocking solution: atomic class (i.e. CAS+volatile)

2. Visibility:

JIT compilation caches the hot code into the working memory, so that the latest data in the main memory is not stored in the working memory

Solution:

  1. volatile to ensure visibility and order
  2. With synchronized, JIT compilation is prohibited, and this part of the code is cached in the working memory, no matter who locks it
  3. Disable JIT globally (not recommended)

3. Order:

cpu adjusts instruction order

For example, load a and b for a while, and cycle 1000 times; When cpu finds that a single thread does not affect the result, it must first load a 1000 times and then load b 1000 times

int a = 0;    
boolean flag = false;    
public void writer( ){    
    a = 1;                 
    flag = true;   
}    
	
public void reader( ){    
    if(flag){       
	int i = a + a; 
    }    
}

Solution:

Locked, synchronized or AQS

To sum up:

  • volatile can solve the problem of visibility & order

  • Optimistic lock CAS can solve the problem of atomicity, visibility and order

  • Pessimistic lock synchronized or AQS can solve the problem of atomicity & visibility & ordering

  • Using private and final modification methods can avoid some problems, because otherwise, in subclasses, creating new threads and allowing new threads to share list local variables will be a disaster

  • The as if serial rule ensures that the instruction rearrangement in a single thread will not affect the execution result

    The happens before rule ensures that the instruction rearrangement under multithreading will not affect the execution result

Happens before rule:

It specifies which write operations are visible to the read operations of other threads. It is a summary of a set of rules for visibility and ordering

One operation happens before another operation, indicating that the result of the first operation is visible to the second operation

  1. The writing of the variable before the thread unlocks m is visible to the reading of the variable by other threads that lock m next
  2. The writing of volatile variable by thread is visible to the reading of this variable by other threads
  3. The writing of the variable before the thread starts is visible to the reading of the variable after the thread starts
  4. The writing of variables before the end of a thread is visible to the reading of other threads after they know that it ends (for example, other threads call t1.isAlive() or T1 Join() wait for it to end)
  5. Thread t1 writes to the variable before interrupting t2(interrupt), which is visible to other threads after they know that T2 is interrupted (through t2.interrupted or t2.isInterrupted)
  6. The writing of the default value of the variable (0, false, null) is visible to the reading of the variable by other threads
  7. Transitivity, if X - > y and Y - > Z, then there is X - > Z

Underlying principle of volatile:

It's a lighter lock

Visibility: when a variable modified by volatile performs a write operation: immediately brush the data of this variable in the working memory to the main memory, and invalidate the cache of this variable in the working memory of other threads

Orderliness: shared variables modified by volatile will join the memory barrier when they are read and written, preventing other read and write operations from crossing the barrier

volatile read-write barriers can only prevent instruction rearrangement in the same thread

The sorting rules of volatile in JMM memory model are:

  • When the first operation is volatile read, no matter what the second operation is, it cannot be reordered

  • When the second operation is volatile write, no matter what the first operation is, it cannot be reordered

  • When the first operation is volatile write and the second operation is volatile read, reordering cannot be performed

JMM(Java memory model)

JMM defines the visibility and order of data;

framework:

  1. Working memory thread private
  2. The main memory thread is public

Memory barrier:

Load is read and Store is write

  1. LoadLoad

    Load1, LoadLoad, Load2; That is, first load1, then load2

  2. StoreStore

    Store1, StoreStore, Store2; That is, first store1, then store2

  3. LoadStore

    Load1, LoadLoad, Store2; That is, load1 first, then store2

  4. StoreLoad

    Store1, StoreStore, Load2; That is, first store1, then load2

Transfer case:

public class ExerciseTransfer {
    public static void main(String[] args) throws InterruptedException {
        Account a = new Account(1000);
        Account b = new Account(1000);
        Thread t1 = new Thread(( ) -> {
            for (int i = 0; i < 1000; i++) {
                a.transfer(b, randomAmount( ));
            }
        }, "t1");
        Thread t2 = new Thread(( ) -> {
            for (int i = 0; i < 1000; i++) {
                b.transfer(a, randomAmount( ));
            }
        }, "t2");
        t1.start( );
        t2.start( );
        t1.join( );
        t2.join( );
        log.debug("total:{}", (a.getMoney( ) + b.getMoney( )));
    }

    static Random random = new Random( );

    public static int randomAmount( ) {
        return random.nextInt(100) + 1;
    }
}

class Account {
    private int money;

    public Account(int money) {
        this.money = money;
    }

    public int getMoney( ) {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void transfer(Account target, int amount) {
        // Note that this cannot be locked because a thread a.transfer(b, randomAmount());, Another thread b.transfer(a, randomAmount());
        // When this is a, b of thread 2 ignores this(a) lock at all
        synchronized (Account.class) {
            if (this.money > amount) {
                this.setMoney(this.getMoney( ) - amount);
                target.setMoney(target.getMoney( ) + amount);
            }
        }
    }
}

IV CAS and atomic class:

CAS:

CAS(compare and set/swap) is an optimistic lock. Its essence is not a lock, but a while loop to judge the version number

Pessimistic lock VS optimistic lock:

Pessimistic locks are implemented by blocking, while optimistic locks use loops instead of blocking

Pessimistic lock: applicable to the scene of fierce lock competition (less reading and more writing)

Advantages: the thread can't grab the lock, and the cpu will be released when it is blocked

Disadvantages: thread switching state has certain overhead

Optimistic lock: it is applicable to the scenario where the lock competition is not fierce (more reading and less writing) and there are more cpu cores

Advantages: threads do not need to switch states

Disadvantages: the thread always executes while, occupying cpu

  • CAS is based on the idea of optimistic locking: I'm not afraid of other threads to modify shared variables. Even if I do, it doesn't matter. I'll try again at a loss
  • Synchronized is based on the idea of pessimistic lock: prevent other threads from modifying shared variables. When I lock, you don't want to change it. After I change it and know how to unlock it, you will have a chance

Atomic class:

The bottom layer uses CAS+volatile to realize lock free concurrency

Four atomic classes in the JUC package:

  • Basic type

    Update base type

    • AtomicInteger: shaping atomic classes
    • AtomicLong: long integer atomic class
    • AtomicBoolean: Boolean atomic class
  • Array type

    Update an element in the array

    • AtomicIntegerArray: shaping array atomic class
    • AtomicLongArray: long integer array atomic class
    • AtomicReferenceArray: reference type array atomic class
  • reference type

    • AtomicReference
    • AtomicStampedReference:
    • AtomicMarkableReference:
  • Update the fields of the class

    It needs to be used with volatile, which modifies this field

    • AtomicIntegerFieldUpdater: updater for integer fields
    • AtomicLongFieldUpdater: updater for long integer fields.
    • AtomicReferenceFieldUpdater: updater of reference type

ABA question:

The value a becomes B and then back to A. do these two a think it is the same version?

AtomicReference and AtomicMarkableReference think so (that is, there is an ABA problem), and AtomicStampedReference thinks not

Solution: use version number

LongAdder:

The accumulation efficiency of LongAdder is higher than AtomicLong:

LongAdder is divided into base and cells:

  1. When there is no concurrent competition or the cell array is being initialized, CAS will be used to accumulate to the base
  2. When there is concurrent competition, the cells array will be initialized. The number of cells in the array can be modified in parallel by how many threads are allowed; Finally, add up each cell in the array and add base to get the final value

It is equivalent to reducing the granularity of the lock, locking only the cells accumulation unit, rather than locking all the cells

UnSafe:

This class is very low-level and we are not recommended to use it directly

The direct memory of CAS and NIO is realized by calling UnSafe at the bottom

V Common thread safety classes:

String

Integer

StringBuffer

Random

Vector

Hashtable

java. util. Classes under concurrent package

Safety here means that multiple threads operating on the same instance will not cause thread safety problems

String and Integer are immutable classes. The so-called modification is just the return of a new object

For Hashtable, a single API is atomic, but the combination cannot guarantee its atomicity,

As shown in the following figure: the original idea was to put the value only when table was null. As a result, because the API combination is not atomic, it was put twice

Vi ReentrantLock:

characteristic:

  1. Multiple Condition variables are supported, that is, there are multiple WaitSet lounges, and you can only wake up the threads of one lounges

    await signal signalall

  2. You can use tryLock

  3. Manual release lock

  4. Reentrant

  5. You can set the timeout

  6. Can be set to fair lock

  7. Interruptible

ReentrantReadWriteLock:

A read lock is a shared lock, a write lock is an exclusive lock, and a write lock can be downgraded to a read lock

VII Callable, Future:

Callable:

// There is a return value; If an exception is thrown, the subclass does not have to try catch
class MyThread implements Callable<Integer> {
    @Override
    public Integer call( ) throws Exception {
        return 200;
    }
}

Future:

  • When you need to perform time-consuming operations in the main thread but do not want to block the main thread, you can hand over these jobs to Future to complete in the background,

    When the main thread needs it in the Future, the result can be obtained through the get method of Future

  • Treat Future as an object to save the results. It does not save the results temporarily, but will save them in the Future (that is, when Callable returns). In order to obtain the results, Callable is required

  • Class relationship: class futuretask < V > implements runnablefuture < V >
    interface RunnableFuture<V> extends Runnable, Future<V>

Related API:

FutureTask<Long> futureTask2 = new FutureTask(callable);

  1. public boolean cancel(boolean flag) / / used to stop tasks
    • If not already started, it will stop the task
    • If it has been started, the task will be forcibly stopped only when the flag is true
  2. public Object get() / / used to obtain the result of a task. It is calculated only once
    • If the task is completed, it will immediately return results
    • Otherwise, it will block, wait for the task to complete, and then return the result; So the general get method is put at the end
  3. public boolean isDone() / / judge whether the task is completed

CompletableFuture:

It is the implementation class of Future. It is used to arrange the relationship between multiple threads and has the function of asynchronous callback

VIII Fork/Join:

Fork: split a complex task, recursively

Join: merge the results of split tasks

Case: calculate 1 + 2 + 3... + 1000, split it into subtasks, and add 100 numbers to each subtask

class TaskExample extends RecursiveTask<Long> {
    private int start;
    private int end;
    private long sum;

    public TaskExample(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute( ) {
        System.out.println("task" + start + "=========" + end + "Accumulation start");
        //More than 100 numbers are added and divided, less than direct addition
        if (end - start <= 100) {
            for (int i = start; i <= end; i++) {
                //accumulation
                sum += i;
            }
        } else {
            //Cut into 2 pieces
            int middle = start + 100;
            //Recursive call, split into 2 small tasks
            TaskExample taskExample1 = new TaskExample(start, middle);
            TaskExample taskExample2 = new TaskExample(middle + 1, end);
            //Execution: asynchronous
            taskExample1.fork( );
            taskExample2.fork( );
            //Get execution results from synchronization blocking
            sum = taskExample1.join( ) + taskExample2.join( );
        }
        //Return after adding
        return sum;
    }
}

public class ForkJoinPoolDemo {

    public static void main(String[] args) {
        //Define task
        TaskExample taskExample = new TaskExample(1, 1000);
        //Object definition execution
        ForkJoinPool forkJoinPool = new ForkJoinPool( );
        //Join task execution
        ForkJoinTask<Long> result = forkJoinPool.submit(taskExample);
        //Output results
        try {
            System.out.println(result.get( ));
        } catch (Exception e) {
            e.printStackTrace( );
        } finally {
            forkJoinPool.shutdown( );
        }
    }
}

IX Thread pool:

Why use thread pools:

  1. Reuse Thread objects to prevent frequent creation and destruction, so as to reduce resource consumption and improve response speed

  2. Improve the manageability of threads and control their upper limit

Seven parameters:

  1. corePoolSize number of core threads
  2. maximumPoolSize maximum number of threads
  3. keepAliveTime the lifetime of the emergency thread
  4. Unit time unit the survival time unit of the emergency thread
  5. workQueue wait queue
  6. threadFactory thread factory can customize the creation of Thread objects, such as setting thread name, whether it is a guard thread, etc
  7. handler reject policy
    1. Throw exception AbortPolicy
    2. Return to the caller to execute CallerRunsPolicy
    3. Discard this task DiscardPolicy
    4. Discard the earliest queued task DiscardOldestPolicy

The emergency thread gives priority to new tasks rather than tasks in the waiting queue

Several methods of creating thread pool:

  1. Create using Executors factory method

    Infinity here means the maximum value of int

    1. newSingleThreadExecutor: single thread
    2. newFixedThreadPool: fixed size but infinite waiting queue
    3. newCachedThreadPool: a thread pool with emergency threads but an infinite number of threads
    4. newScheduledThreadPool: the number of threads is unlimited and supports scheduled scheduling
  2. It is created by passing parameters through the new ThreadPoolExecutor method

Thread destruction:

The emergency thread is automatically destroyed after being idle for a certain time

The core thread can be destroyed by calling shutdown and other methods

  1. shutdown

    • Do not receive new tasks

    • The submitted task will be completed

    • Threads calling this method are not blocked

    • The thread pool state changes to SHUTDOWN

  2. shutdownNow

    • Do not receive new tasks

    • Tasks that have been submitted but are still in the queue and have not started execution will be cancelled

    • Interrupt an executing task with interrupt

    • The thread pool status changes to STOP

X Locks commonly used in AQS:

AQS: AbstractQueuedSynchronizer Abstract queue synchronizer

Reentrantlock, futuretask, countdownlatch, cyclicbarrier and semaphore are all implemented based on AQS

AQS can be exclusive or shared

There are some CAS codes inside AQS

Principle of AQS:

AQS maintains a shared resource, and then uses a two-way queue to ensure that threads queue to obtain resources

The volatile state field is used to represent the synchronization state, and the CAS is used for setState

Only the head node in the queue is the holder of the lock, and the tail pointer points to the last waiting thread node in the queue

What are the resource sharing methods of AQS?

This is achieved by modifying the state field

  • Exclusive: exclusive. Only one thread can execute, such as ReentrantLock
  • Share: share. Multiple threads can execute simultaneously, such as ReentrantReadWriteLock

AQS uses a template approach to design patterns

Custom AQS:

  1. Inherit AbstractQueuedSynchronizer

  2. Rewrite method:

    1. Ishldexclusively(): judge whether the thread is monopolizing resources

    2. tryAcquire(int): exclusive mode, trying to obtain resources

    3. tryRelease(int): exclusive mode, trying to release resources

    4. Tryacquiresered (int): sharing mode, trying to obtain resources; A negative number indicates failure, 0 indicates no available resources, and a positive number indicates available resources

    5. Tryrereleaseshared (int): sharing mode, trying to release resources

Countdownlatch:

When the value of the counter becomes 0, the thread blocked by the await method will be awakened and continue to execute

Case: after six students leave the classroom one after another, the students on duty can close the door

public class CountDownLatchDemo {

    public static void main(String[] args) throws Exception {
        //Define a counter with a value of 6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        //Create 6 students (threads)
        for (int i = 1; i <= 6; i++) {
            new Thread(( ) -> {
                try {
                    if (Thread.currentThread( ).getName( ).equals("Classmate 6")) {
                        Thread.sleep(2000);
                    }
                    System.out.println(Thread.currentThread( ).getName( ) + "I am leaving");
                    //If the counter is decremented by one, it will not block
                    countDownLatch.countDown( );
                } catch (Exception e) {
                    e.printStackTrace( );
                }
            }, "classmate" + i).start( );
        }
        countDownLatch.await( ); // When the counter is 0, it is awakened
        System.out.println("All left,The current counter is" + countDownLatch.getCount( ));
    }
}

Cyclicbarrier:

Every execution of cyclicbarrier Await() once, the number of obstacles will be increased by one. If the target number of obstacles is reached, it will be executed

Constructor: public cyclicbarrier (int parties, runnable, runnable)

Case: experience 81 difficulties to obtain scriptures

public class CyclicBarrierDemo {

    private final static int NUMBER = 81;

    public static void main(String[] args) throws Exception{
        //After defining the loop fence and the defined run method (lambda expression), await() will be executed 81 times
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ( ) -> System.out.println("Gather together" + NUMBER + "Dragon Ball,Now summon the Dragon!!!!!!!!!"));
        //Define 7 threads to collect dragon beads respectively
        for (int i = 1; i <= NUMBER; i++) {
                try {
                    System.out.println("Experience No" + i+"hard");
                    Thread.sleep(500L)
                    cyclicBarrier.await( );
                } catch (Exception e) {
                    e.printStackTrace( );
                }
        }
    }
}

Semaphore:

Define the maximum license. Each semaphore can have at most one license at the same time.

Use the acquire method to obtain the license, and the release method to release the license

Case: 6 cars (threads) grab 3 parking spaces

public class SemaphoreDemo {

    public static void main(String[] args) throws Exception {
        //Define 3 parking spaces
        Semaphore semaphore = new Semaphore(3);
        //Simulate the parking of 6 cars
        for (int i = 1; i <= 6; i++) {
            Thread.sleep(100);
            //parking
            new Thread(( ) -> {
                try {
                    System.out.println(Thread.currentThread( ).getName( ) + "Find a parking space ing");
                    semaphore.acquire( );
                    System.out.println(Thread.currentThread( ).getName( ) + "Car parking success!");
                    Thread.sleep(10000L); //Parking
                } catch (Exception e) {
                    e.printStackTrace( );
                } finally {
                    System.out.println(Thread.currentThread( ).getName( ) + "Slip away, slip away");
                    semaphore.release( );
                }
            }, "automobile" + i).start( );
        }
    }
}

Postmark lock StampedLock:

It is the optimization of read-write lock. It is optimistic. It is similar to CAS, and the stamp is the version number

  1. Optimistic reading:

    long stamp = lock.tryOptimisticRead( );

    //Check stamp

    if(!lock.validate(stamp)){

    //Lock upgrade

    }

  2. Read lock:

    long stamp = lock.readLock( );

    lock.unlockRead(stamp);

  3. Write lock:

    long stamp = lock.writeLock( );

    lock.unlockWrite(stamp);

Xi Big contrast:

wait VS sleep:

  1. wait must be put in synchronized and used together with Monitor, while sleep does not
  2. wait is a method in Object, while sleep is a static method in Thread
  3. wait releases the lock, while sleep does not

wait VS park:

  1. Wait and notify must be put in synchronized and used together with Monitor, while Park and unpark do not have to

  2. wait is the method in Object, and park is the method in LockSuport

  3. notify can only wake up one waiting thread randomly. notifyAll wakes up all waiting threads, which is not so accurate;

    Park and unpark are used to block and wake up threads in the unit of threads, which is more accurate

  4. Wait & notify cannot notify first, but Park & unpark can unpark first

  5. wait releases the lock, but park does not

Interrupt the threads of sleep, wait, join and Park:

  1. interrupt() / / interrupt the thread
    • Interrupt the thread that is in sleep, wait and join, throw InterruptedException and clear the interrupt flag
    • If a running or park thread is interrupted, the interrupt flag will be set
  2. isInterrupted() / / judge whether the thread is interrupted. The interrupt flag will not be cleared
  3. / / if the thread is interrupted, the interrupt flag () will be cleared

synchronized VS volatile:

  1. The bottom layer of synchronized relies on Monitor to ensure atomicity, visibility and order, and the operation is heavy

    The bottom layer of volatile is to cache data to working memory by disabling JIT compilation to ensure visibility, and to ensure order by reading and writing memory barrier, but there is nothing to do with atomicity; Light operation

  2. synchronized acts on code blocks or methods, and volatile acts on variables

  3. synchronized will cause thread blocking, while volatile will not cause thread blocking

synchronized VS Lock:

  1. The bottom layer of synchronized is that Monitor is implemented by C + +, while Lock is implemented by Java

  2. synchronized will release the Lock automatically, and Lock needs to release the Lock manually (put it in finally)

  3. Lock has richer functions, such as:

    • tryLock
    • Condition (equivalent to multiple waitsets, which can wake up the threads in the specified WaitSet)
    • Read / write lock (finer lock granularity and shared read)
  4. Performance:

    • When there is no competition, synchronized makes many optimizations, such as biased lock and lightweight lock; Good performance

    • Lock performs better when the competition is fierce (such as read sharing)

Runable VS Thread:

  1. Runable is the interface, Thread is the class, and runable is the parent interface of Thread
  2. The Thread of Runable needs to be started by the Thread agent, and the Runable implementation class is passed to the Thread constructor as a parameter
  3. Because of Java's single inheritance and multiple interfaces, Runable is preferred

Runable run( ) VS Callable call( ):

  1. run has no return value, while call has a return value, so it can be used in combination with Future
  2. The run method itself does not throw an exception, so the subclass can only try catch, while the call method itself throws an exception, and the subclass does not have to try catch

execute VS submit:

The execute() method is used to submit tasks that do not need a return value, while the submit() method is used to submit tasks that need a return value

Therefore: the execute() method can only execute tasks of Runnable type, while the submit() method can execute tasks of Runnable and Callable types

Deadlock VS livelock:

A deadlock is when two threads wait for each other and are in a blocked state

The livelock is not blocked, it is moving all the time, and it is constantly trying, but the conditions have not been met

XII Code case:

Thread eight lock problem:

  1.  import java.util.concurrent.TimeUnit;
     
     //byPlane first, then byCar. In fact, the two methods are locked at the same time, but there is no time to pause. I can't see it
     public class Sync_1 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_A vehicle1 = new Vehicle_A( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //Just to eliminate the error that threads start successively, thread 1 is considered to start earlier than thread 2
             new Thread(( ) -> vehicle1.byCar( )).start( );
         }
     
     }
     
     class Vehicle_A {
     
         public synchronized void byPlane( ) {
             System.out.println("------byPlane------");
         }
     
         public synchronized void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  2.  import java.util.concurrent.TimeUnit;
     
     //After 3s, byPlane and byCar explain that the two methods are locked at the same time
     public class Sync_2 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_B vehicle1 = new Vehicle_B( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //Just to eliminate the error of thread starting successively
             new Thread(( ) -> vehicle1.byCar( )).start( );
         }
     
     }
     
     class Vehicle_B {
     
         public synchronized void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  3.  import java.util.concurrent.TimeUnit;
     
     //First hello, 3s and then byPlane. Explain that the two methods are not locked at the same time
     public class Sync_3 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_C vehicle1 = new Vehicle_C( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //Just to eliminate the error of thread starting successively
             new Thread(( ) -> vehicle1.hello( )).start( );
         }
     
     }
     
     class Vehicle_C {
     
         public synchronized void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized void byCar( ) {
             System.out.println("------byCar------");
         }
     
         public void hello( ) {
             System.out.println("------hello------");
         }
     
     }
    
  4.  import java.util.concurrent.TimeUnit;
     
     //Bycar first, 3S later byPlane, and then explain that the two methods are not locked at the same time
     public class Sync_4 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_D vehicle1 = new Vehicle_D( );
             Vehicle_D vehicle2 = new Vehicle_D( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //Just to eliminate the error of thread starting successively
             new Thread(( ) -> vehicle2.byCar( )).start( );
         }
     
     }
     
     class Vehicle_D {
     
         public synchronized void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  5.  import java.util.concurrent.TimeUnit;
     
     //After 3s, byPlane and byCar explain that the two methods are locked at the same time
     public class Sync_5 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_E vehicle1 = new Vehicle_E( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //Just to eliminate the error of thread starting successively
             new Thread(( ) -> vehicle1.byCar( )).start( );
         }
     
     }
     
     class Vehicle_E {
     
         public synchronized static void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized static void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  6.  import java.util.concurrent.TimeUnit;
     
     //After 3s, byPlane and byCar explain that the two methods are locked at the same time
     public class Sync_6 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_F vehicle1 = new Vehicle_F( );
             Vehicle_F vehicle2 = new Vehicle_F( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //Just to eliminate the error of thread starting successively
             new Thread(( ) -> vehicle2.byCar( )).start( );
         }
     
     }
     
     class Vehicle_F {
     
         public synchronized static void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized static void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  7.  import java.util.concurrent.TimeUnit;
     
     //Bycar first, 3S later byPlane, and then explain that the two methods are not locked at the same time
     public class Sync_7 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_G vehicle1 = new Vehicle_G( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //Just to eliminate the error of thread starting successively
             new Thread(( ) -> vehicle1.byCar( )).start( );
         }
     
     }
     
     class Vehicle_G {
     
         public synchronized static void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public static void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  8.  import java.util.concurrent.TimeUnit;//First byCar, 3s and then byplane. Explain that the two methods public class sync are not locked at the same time_ 8 {public static void main (string [] args) throws interruptedexception {vehicle_h vehicle1 = new vehicle_h(); vehicle_h vehicle2 = new vehicle_h(); new thread (() - > vehicle1. Byplane()). Start(); timeunit. Milliseconds. Sleep (500L); / / just to eliminate the error that threads start successively. New thread (()  -> vehicle2. byCar( )). start( );    }} class Vehicle_ H {    public synchronized static void byPlane( ) {        try {            TimeUnit.SECONDS.sleep(3);        }  catch (InterruptedException ignored) {        }        System. out. println("------byPlane------");    }     public synchronized void byCar( ) {        System.out.println("------byCar------");    }}
    

Summary:

  1. The non static method of lock is lock this, and the static method of lock is the corresponding class of lock class

  2. Locking static does not affect non static methods, but affects other static methods

  3. Locking non static does not affect static, but affects other non static

Multiple threads alternate printing problem:

  1. Three threads T1, T2 and T3 print A, B and C alternately for 10 times, such as abcabcabcabcabc

  2. Three threads T1, T2 and T3 print A(5 times), B(10 times) and C(15 times) alternately for 10 times, Such as aaaaaabbbbbbbbbbbbcccccccccccccacaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

  3. Three threads alternately print 1-100, T1 print 1, T2 print 2, T3 print 3, T1 print 4

  4. Two threads print letters and numbers alternately, T1 prints A1, T2 prints B2, T1 prints C3... All the way to Z26, such as A1B2C3

Synchronized & wait & notify solution:

  1.   public class Demo01 {
      
          private static Object lock = new Object( );
          private static int num;
      
          public static void printABC(String show, int targetNum) throws InterruptedException {
              for (int i = 0; i < 10; i++) {
                  synchronized (lock) {
                      while (num % 3 != targetNum) {
                          lock.wait( );
                      }
                      num++;
                      System.out.print(show);
                      lock.notifyAll( );
                  }
              }
          }
      }
      
      class Test01 {
      
          public static void main(String[] args) {
              new Thread(( ) -> {
                  try {
                      Demo01.printABC("A", 0);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
              new Thread(( ) -> {
                  try {
                      Demo01.printABC("B", 1);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
              new Thread(( ) -> {
                  try {
                      Demo01.printABC("C", 2);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
          }
      }
    
  2.  public class Demo02 {
     
     
         private static Object lock = new Object( );
         private static int num;
     
         public static void printABC(String show, int targetNum) throws InterruptedException {
             for (int i = 0; i < 10; i++) {
                 synchronized (lock) {
                     while (num % 3 != targetNum) {
                         lock.wait( );
                     }
                     num++;
                     for (int j = 0; j < 5 * (targetNum + 1); j++) {
                         System.out.print(show);
                     }
                     lock.notifyAll( );
                 }
             }
         }
     }
     
     class Test02 {
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("A", 0);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("B", 1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("C", 2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  3.  public class Demo03 {
     
         private static Object lock = new Object( );
         private static int num;
     
         public static void print_1_100(String threadName, int targetNum) throws InterruptedException {
             // Print num after num [0,99] num + +
             while (true) {
                 synchronized (lock) {
                     while (num % 3 != targetNum) {
                         lock.wait( );
                     }
                     num++;
                     if (num > 100) {
                         // If you do not wake up other threads, other threads will be wait ing and the program will not end
                         lock.notifyAll( );
                         break;
                     }
                     System.out.println(threadName + ":" + num);
                     lock.notifyAll( );
                 }
             }
         }
     }
     
     class Test03 {
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("T1", 0);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("T2", 1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("T3", 2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  4.  public class Demo04 {
          public static Object lock = new Object( );
          public static int num;
      
          public static void print(int order) throws InterruptedException {
              while (true) {
                  synchronized (lock) {
                      while (num % 2 != order) {
                          lock.wait( );
                      }
                      num++;
                      if (num > 26) {
                          lock.notifyAll( );
                          break;
                      }
                      System.out.print((char) (num - 1 + 'A'));
                      System.out.print(num);
                      lock.notifyAll( );
                  }
              }
          }
      }
      
      class Test04 {
      
          public static void main(String[] args) {
              new Thread(( ) -> {
                  try {
                      Demo04.print(0);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
              new Thread(( ) -> {
                  try {
                      Demo04.print(1);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
          }
      }
        
    

Solution of lock & condition version:

  1.  public class Demo01 {
     
         private static int num;
     
         public static void printABC(String show, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException {
             for (int i = 0; i < 10; i++) {
                 lock.lock( );
                 try {
                     while (num % 3 != targetNum) {
                         cur.await( );
                     }
                     num++;
                     System.out.print(show);
                     next.signal( );
                 } finally {
                     lock.unlock( );
                 }
             }
         }
     }
     
     class Test01 {
         private static ReentrantLock lock = new ReentrantLock( );
         private static Condition condition1 = lock.newCondition( );
         private static Condition condition2 = lock.newCondition( );
         private static Condition condition3 = lock.newCondition( );
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo01.printABC("A", 0, lock, condition1, condition2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo01.printABC("B", 1, lock, condition2, condition3);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo01.printABC("C", 2, lock, condition3, condition1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  2.  public class Demo02 {
     
         private static int num;
     
         public static void printABC(String show, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException {
             for (int i = 0; i < 10; i++) {
                 lock.lock( );
                 try {
                     while (num % 3 != targetNum) {
                         cur.await( );
                     }
                     num++;
                     for (int j = 0; j < 5 * (targetNum + 1); j++) {
                         System.out.print(show);
                     }
                     next.signal( );
                 } finally {
                     lock.unlock( );
                 }
             }
         }
     }
     
     class Test02 {
         private static ReentrantLock lock = new ReentrantLock( );
         private static Condition condition1 = lock.newCondition( );
         private static Condition condition2 = lock.newCondition( );
         private static Condition condition3 = lock.newCondition( );
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("A", 0, lock, condition1, condition2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("B", 1, lock, condition2, condition3);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("C", 2, lock, condition3, condition1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  3.  public class Demo03 {
     
         private static int num;
     
         public static void print_1_100(String threadName, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException {
             // Print num after num [0,99] num + +
             while (true) {
                 lock.lock( );
                 try {
                     while (num % 3 != targetNum) {
                         cur.await( );
                     }
                     num++;
                     if (num > 100) {
                         // If you do not wake up other threads, other threads will be wait ing and the program will not end
                         next.signal( );
                         break;
                     }
                     System.out.println(threadName + ":" + num);
                     next.signal( );
                 } finally {
                     lock.unlock( );
                 }
             }
         }
     }
     
     class Test03 {
         private static ReentrantLock lock = new ReentrantLock( );
         private static Condition condition1 = lock.newCondition( );
         private static Condition condition2 = lock.newCondition( );
         private static Condition condition3 = lock.newCondition( );
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("A", 0, lock, condition1, condition2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("B", 1, lock, condition2, condition3);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("C", 2, lock, condition3, condition1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  4.  public class Demo04 {
          public static int num;
      
          public static void print(int order, Lock lock, Condition cur, Condition next) throws InterruptedException {
              while (true) {
                  lock.lock( );
                  try {
                      while (num % 2 != order) {
                          cur.await( );
                      }
                      num++;
                      if (num > 26) {
                          next.signal( );
                          break;
                      }
                      System.out.print((char) (num - 1 + 'A'));
                      System.out.print(num);
                      next.signal( );
                  } finally {
                      lock.unlock( );
                  }
              }
          }
      }
      
      class Test04 {
          private static ReentrantLock lock = new ReentrantLock( );
          private static Condition condition1 = lock.newCondition( );
          private static Condition condition2 = lock.newCondition( );
      
          public static void main(String[] args) {
              new Thread(( ) -> {
                  try {
                      Demo04.print(0, lock, condition1, condition2);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
              new Thread(( ) -> {
                  try {
                      Demo04.print(1, lock, condition2, condition1);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
          }
      }  
    

Topics: Java JUC