Prerequisite Review

../3.应用程序开发/多线程 Java SE/3. Application Development/Multithreading > Implement the Callable interface Common THREAD methods

Essentially all are Thread class calls start(),start()methods to start a new thread

  1. Inherit Thread the class, override run the method
  2. Pass to Thread the class runnable the implementation class, override run the method
  3. Create Callable(实现call方法)-->pass to FutureTask-->pass to Thread or directly call.call(): execute directly in the current thread Callable the task (synchronous execution), this is just an ordinary method call, executed synchronously in the current thread, not multithreading (solving the traditional Runnable interface problem of not being able to return values and throw checked exceptions.)
  4. Executors Create a thread pool

CompletableFuture does not directly create threads, it is a wrapper around a thread pool (usually ForkJoinPool.commonPool()) , utilizes multithreading (or a thread pool) to implement asynchronous programming.

When using CompletableFuture asynchronous methods (such as supplyAsync, runAsync), it submits the task to the thread pool for execution

assets/JUC并发编程/file-20240909000006600.png

Java SE/3. Application Development/Multithreading > Implement the Callable interface

concurrency (concurrent) the same entity multiple events,one processing multiple tasks on a processor simultaneously (large scale data parallel processing in scientific computing and image processing ) parallelism (parallel): different entity multiple events, multiple processors processing multiple tasks (a Web server handling multiple client requests ) ../3.应用程序开发/assets/多线程/image-20231212213415636.png ../4.面对对象编程/3.应用程序开发/assets/多线程/image-20231212213420115.png

parallel then concurrent

monitor : Monitor monitor, which is what is usually called a lock JVM relies on the monitor to implement method level synchronization and synchronization of a sequence of instructions within a method

assets/JUC并发编程/image-20240415163028634.png

user thread : system worker thread daemon thread : serves other threads, such as GC

isDaemon determine whether a thread is a userfalseor daemon true assets/JUC并发编程/image-20240415164207070.png

CompletableFuture

CompletableFuture does not directly create threads, it is a wrapper around a thread pool (usually ForkJoinPool.commonPool()) , utilizes Multi-threading (or a thread pool) to implement asynchronous programming.

CompletableFuture is Future an enhanced version of that, reducing blocking and polling. It can accept a callback object, and when an asynchronous task completes or an exception occurs, it automatically invokes the callback method of the callback object.

Requirement: multi-threading, able to return, able to handle asynchronous tasks, which ordinary threads cannot satisfy.

Future Interface

FutureTask is Future a concrete implementation class of the interface, both representing an asynchronous computationresult

futureTask Regarding the issue of obtaining the result

get there is a blocking problem isDone() the method easily consumes CPU resources


ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Integer> future = executor.submit(() -> { Thread.sleep(2000); // Simulate a time consuming operation return 42; })

../3.应用程序开发/assets/多线程/image-20240416105158341.png ../4.面对对象编程/3.应用程序开发/assets/多线程/image-20240416105237771.png

The polling approach above is not elegant, and the following requirements should also be met

  1. On completion it can actively notify
  2. Able to combine different asynchronous results
  3. Able to select the one that completes fastest

assets/JUC并发编程/image-20240416105909440.png assets/JUC并发编程/image-20240416170938205.png

CompletionStage Interface

CompletionFuture is CompletionStage a concrete implementation class of the interface and Future of the interface, providing more functions and operation methods tohandleasynchronous computation tasks.

assets/JUC并发编程/image-20240416174948778.png assets/JUC并发编程/image-20240416175221945.png

  1. Asynchronous task finishes/error occurs it automatically invokes the method of some object;
  2. after the main thread sets up the callback, it no longer cares about the execution of the asynchronous task, and asynchronous tasks can execute in sequence.
  • runAsync
  • supplyAsync

Not recommended to use CompatableFuture the default no-argument creation, because created this way the CompletableFuture instance does not specify a concrete asynchronous task, which may lead to logical errors or exceptions during later use. The correct approach is, when creating the CompletableFuture instance, to pass in a concrete asynchronous task, to ensure the correctness and stability of the program. assets/JUC并发编程/image-20240416180038146.png

If using runAsyncthen again get it is still the previous blocking style

runAsync

assets/JUC并发编程/image-20240416184711733.png

thread pool similar to daemon thread will Main end immediately after the thread ends, so ensure that the main thread ends last

assets/JUC并发编程/image-20240416215142982.png

CompletableFuture

whenComplete


    public static void main(String[] args) throws ExecutionException, InterruptedException
    {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try
        {
            CompletableFuture.supplyAsync(
                    () -> {
                System.out.println(Thread.currentThread().getName() + "----come in");
                int result = ThreadLocalRandom.current().nextInt(10);
                try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                System.out.println("-----1秒钟后出结果:" + result);
                if(result > 2)
                {
                    int i=10/0;
                }
                return result;
            },threadPool)
			        //v is the return value above
                    .whenComplete((v,e) -> {
                if (e == null) {
                    System.out.println("-----计算完成,更新系统UpdateValue:"+v);
                }
            }).exceptionally(e -> {
                e.printStackTrace();
                System.out.println("异常情况:"+e.getCause()+"\t"+e.getMessage());
                return null;
            });

            System.out.println(Thread.currentThread().getName()+"线程先去忙其它任务");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
        //the main thread should not end immediately, otherwise CompletableFuture the default thread pool used will close immediately:pause 3 seconds thread
        //try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
    }

E-commerce website price comparison, to begin with

../新特性/JDK8-17新特性#2 函数式接口

assets/JUC并发编程/image-20240416222217500.png

The conversion uses a multi-threading case, simulating a query

Take theliststored inList<NetMall>out and convert it toList<CompletableFuture<String>>then convert toList<String>

This uses the intermediate method map in stream Java SE/新特性/Stream流#中间方法

assets/JUC并发编程/image-20240416235732109.png Convert the NetMall object into a CompletableFuture object, and return a CompletableFuture object representing the asynchronous computation result

The version not using lambda

assets/JUC并发编程/image-20240708165443581.png


public static List<String> getPriceByCompletableFuture(List<NetMall> list,String productName)  
{  
    return list.stream().map(netMall -> CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",netMall.getNetMallName(),netMall.calcPrice(productName)))).collect(Collectors.toList())//terminal method, collecting the result of the first computation into a list  
            .stream()//call again stream()method to convert the list into a new stream  
            .map(s -> s.join())//then,use map()method converts each element in the stream s performs transformation operations  
            .collect(Collectors.toList());//collects all objects into a list  
}

CompatableFuture Common methods

  • runAsync
  • supplyAsync
  • get()
  • get()
1 Obtain result and trigger computation

assets/JUC并发编程/image-20240708211402813.png

2 Process the computation result

thenApply、handle Both have the same effecthandlecan handle exceptions assets/JUC并发编程/image-20240708213406427.png

assets/JUC并发编程/image-20240708214311150.png assets/JUC并发编程/image-20240708214418769.png

3 Consume the computation result

thenRun does not need the result of the previous step, will use the previous thread pool thenRunAsync asynchronous, will useForkJoinPool, does not use a custom thread pool thenAcceptneeds the result of the previous step, no return value, it returns a new CompletableFuture<Void> thenApplyneeds the result of the previous step, has a return value thenRunAsync will use ForkJoinPool, does not use a custom thread pool

assets/JUC并发编程/image-20240708215422472.png join() the method blocks the current thread, waiting for the asynchronous task to finish. This ensures that where the asynchronous task result is needed, the final result can be obtained

assets/JUC并发编程/image-20240709002304854.png

  • thenRunAsync

System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());//null  
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(r -> System.out.println(r)).join());//resultA and null,this is thenAccept the return result of  
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(r -> System.out.println(r)));  
//resultA and java.util.concurrent.CompletableFuture@76fb509a[Completed normally] this is thenAccept the return result of ,at this time, since it has not finished running, there is no result
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(r -> r + "resultB").join());//resultAresultB

4 Select based on computation speed

applyToEither A.applyToEither(B, c -> { return c + " is winer"; });

CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {  
    System.out.println("A come in");  
    try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }  
    return "playA";  
});  
  
CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {  
    System.out.println("B come in");  
    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }  
    return "playB";  
});  
  
CompletableFuture<String> result = playA.applyToEither(playB, f -> {  
    return f + " is winer";  
});  
  
System.out.println(Thread.currentThread().getName()+"\t"+"-----: "+result.join());
5 Merge the computation results

x.thenCombine(y,(x,y)->{})

after the two CompletionStage tasks complete, the results of the two completableFuture are passed together tothenCombineto process

CompletableFuture result = completableFuture1.thenCombine(completableFuture2, (x, y) -> {
System.out.println("-----开始两个结果合并");
return x + y;
}); assets/JUC并发编程/image-20240710201532281.png

assets/JUC并发编程/image-20240710201524670.png

lock

assets/JUC并发编程/image-20240711002257100.png

assets/JUC并发编程/image-20240711002354882.png assets/JUC并发编程/image-20240711163939641.png

  • synchronized method (lock this) assets/JUC并发编程/image-20240724102306730.png if there are multiple inside one object synchronized methods, as long as one thread calls one of the synchronized methods, other threads of this object can only synchronized methods can only wait that is, for the same object, only one unique thread can access these synchronized methods

if there are two objects, then it is not the same lock, so each calls its own synchronized methods, there will be no effect

  • static synchronized method (lock class template file)

assets/JUC并发编程/image-20240724102316350.png

will add a class lock, all instances will be locked

for ordinary synchronized methods, what is locked is the current instance object, usually referring to this, a specific phone, all ordinary synchronized methods use the same lock>the instance object itself

for static synchronized methods, what is locked is the current classClassobject, such asPhone.classthe unique template

assets/JUC并发编程/image-20240724001336532.png

synchronized

synchronized synchronized code block

bytecode monitorenter/monitorexit to control the entry and exit of the lock one enter, twoexit

if there is an exception, it is one enter, one exit assets/JUC并发编程/image-20240724010315394.png

synchronized ordinary synchronized method

assets/JUC并发编程/image-20240724010955528.png

synchronized static synchronized method

assets/JUC并发编程/image-20240724111755318.png

why every object can become a lock

At the Java bytecode level, the implementation of the synchronized keyword relies onmonitorenterandmonitorexitthese two instructions. Every Java object implicitly has an associated monitor lock (monitor). When executing themonitorenterinstruction,the thread attempts to acquire ownership of the monitor lock associated with the object. When executing themonitorexitinstruction, the thread releases that monitor lock.

Thread execution requires first holding the monitor

assets/JUC并发编程/image-20240724111909703.png

Every object inherently comes with an object monitor, and every locked object is associated with a Monitor

Among them,_ownerwill point to the thread holding the lock; whoever holds it records it, whoever releases it cancels it

assets/JUC并发编程/image-20240724113655465.png assets/JUC并发编程/image-20240724113621705.png

ReentrantLock Fair and non fair locks (default)

Fair: multiple threads acquire the lock in the order they applied for it Non fair lock: does not follow the order of lock application; a thread that applies later may acquire the lock before a thread that applied earlier. In a high concurrency environment, this may cause priority inversion or lock starvation

assets/JUC并发编程/image-20240724122115730.png assets/JUC并发编程/image-20240724122108011.png T

Why ReentrantLock non fair locks by default
  1. Non fair locks reduce thread switching overhead
  2. Non fair locks have high utilization

assets/JUC并发编程/image-20240724122242084.png

The choice between fair and non fair

Non fair: throughput, saves time Fair: everyone gets an equal share

Reentrant lock (recursive lock)

JAVA microservice ecosystem/Redis7/basics/Redis advanced chapter > Reusable lock

Deadlock cases and how to troubleshoot deadlocks

Mutual exclusion condition/no preemption/hold and wait/circular wait

Two threads each want the other lock assets/JUC并发编程/image-20240725143942227.png

Deadlock troubleshooting

jsp -l jstack Process ID assets/JUC并发编程/image-20240725153538529.png

Or use graphical tools such as jconsole

Section

assets/JUC并发编程/image-20240725160544718.png

LockSupport and thread interruption

assets/JUC并发编程/image-20240725162017815.png

Thread interruption mechanism

interrupt/interrupted/isinterrupted

is a negotiation mechanism, not mandatory; the thread itself decides whether to interrupt assets/JUC并发编程/image-20240725165338533.png interrupt will set the interrupt status to True, and cannot interrupt the thread; it merely acts as a flag bit, and requires manual code cooperation

If the thread itself is in the sleep,wait,join state, and another thread calls interrupt set to true, an exception will be thrown interruptedException, and the exception will clear the interrupt status, changing it to false, so it is necessary to call it again in the exception handling place to ensure thread termination

assets/JUC并发编程/image-20240726150418631.png

  • interrupted: returns the current thread interrupt status, and then sets the current status back to false

that is, if the interrupt status is True, calling interrupted returns the current thread interrupt statustrue, and then sets it tofalse

exception interruptedException after it occurs will set interrupt to false, which will cause the true set by other threads to become invalid

assets/JUC并发编程/image-20240726124021034.png

When there is nointerruptwhen interrupting, use the interrupt flag to interrupt

the thread that needs to be interrupted continuously monitors the interrupt status

  • volatile volatile, whenvolatileis true, interrupt the thread, then change the state in another thread

assets/JUC并发编程/image-20240725181449480.png

  • AtomicBoolean

assets/JUC并发编程/image-20240725181600676.png

  • interrupt

it means setting the corresponding thread interrupt to True, and combining it with a while loop for break, which can achieve an effect similar to the above code

the underlying layer is all implemented through isInterrupted implemented

the underlying layer

assets/JUC并发编程/image-20240726161639723.png

LockSupport

LockSupportis the basic thread blocking primitive used to create locks and other synchronization classes

thread wait and wakeup mechanism

  • three ways to make threads wait and wake up assets/JUC并发编程/image-20240726163807829.png assets/JUC并发编程/image-20240726185540020.png

Object the class methods ofwait/notify must appear in pairs in synchronized code blocks or synchronized methods

wait/notify

firstwaitafternotify must appear in pairs in synchronized code blocks or synchronized methods

t1 ----come in t2 ----sends notification t1 ----is awakened

t1 after being awakened, it will try from the wait() method locationreacquire objectLock the lock of

assets/JUC并发编程/image-20240726192724953.png

Java SE/3. Application Development/Multithreading > new Condition

in this example,awaitCondition the method will make the current thread wait until signalCondition the method sends a signal

Lock condition | await/signal

acquire the lock firstLock.lock() condition.await(); condition.signal();

assets/JUC并发编程/image-20240728113049875.png

Object and Condition the limitation is that

  1. the lock must be acquired first orlock
  2. must wait first and then wake up

LockSupport solves the above limitation no lock block is needed supports waking up first and waiting later (permit design)

execute first unpark will set a permit, even if park at unpark executes later, there is already a permit, it is directly released, and one permit will be consumed

unpark will add one permit, which cannot accumulate, there is at most only one, calling multiple times unpark also has only one permit

park and unpark in LockSupport

assets/JUC并发编程/image-20240728184425734.png assets/JUC并发编程/image-20240728113344520.png

assets/JUC并发编程/image-20240728191143764.png assets/JUC并发编程/image-20240728191209487.png

Java Memory Model JMM

  • JMM specification: The JMM specification controls data synchronization between thread working memory and main memory.
  1. When to synchronize data in main memory to thread working memory:
  2. when to flush data in thread working memory to main memory
  3. provides happens-before relationships: JMM defines a set of happens-before rules, these rules mandate happens-before relationships between certain operations,

assets/JUC并发编程/image-20240729173848496.png

JVM shields out various hardware and operating system memory access differences, JVM itself is an abstract concept, describing a set of specifications, the specification defines the read and write access methods of each variable in a program (especially multithreading) and determines when and how a write to a shared variable by one thread becomes visible to another thread, the key technical points all revolve around multithreading atomicity, visibility and ordering。

the three major characteristics of JVM (visibility, atomicity, ordering)

visibility

visibility: once changed, visible to all threads

javaordinary shared variables in do not guarantee visibility, that is to say the mainshared variables in memoryare not withshared variablescopies for synchronization, at this time the modified data needs to be synchronized to main memory, and other threads update from main memory

Modifications made by the main thread are also modifying a variable copy, not directly flushed to main memory, and also require subsequent flushing

assets/JUC并发编程/image-20240729175607858.png assets/JUC并发编程/image-20240729180331580.png assets/JUC并发编程/image-20240729180835692.png

  • Atomicity: an operation cannot be interrupted, and under multithreading it cannot be interfered with by other threads
  • Ordering Code executes in order from top to bottom
Ordering

If data have no mutual dependencies and the execution results are consistent, they can be reordered

assets/JUC并发编程/image-20240729192829524.png assets/JUC并发编程/image-20240729214209368.png It is impossible for statement four to be reordered to the first position

Happens-before

It abstracts complex low-level memory operations and processor behavior into a set of rules we can understand (that is, how the underlying layer executes our code). These rules are the HB rules. You only need to follow these HB rules to ensure the correctness of concurrent programs, without caring about how the underlying layer implements them

If the execution of your code is not within the HB rules, it cannot be guaranteed whether the code is safe when executed

assets/JUC并发编程/image-20240729233645537.png assets/JUC并发编程/image-20240729224950609.png

Case

assets/JUC并发编程/image-20240729234209464.png

To solve it, there are two ways

assets/JUC并发编程/image-20240729234501464.png

assets/JUC并发编程/image-20240729234558829.png

volatile and Java memory model

assets/JUC并发编程/image-20240729235957345.png

write volatile , the JMM immediately flushes the shared variable value in the local memory corresponding to that thread to main memory

read volatile , the JMM sets the shared variable value in the local memory corresponding to that thread to invalid, and directly reads the shared variable from main memory

That is volatile both read and write interact directly with main memory

assets/JUC并发编程/image-20240730120910418.png

Memory barrier

volatile The underlying implementation uses memory barriers

A memory barrier is a barrier instruction that makes the CPU or compiler perform, on the memory operations issued before and after the barrier instruction, anordering constraint, also called a memory fence or fence instruction

Memory barriers guarantee the volatile visibility of, and only guarantee volatile variables

assets/JUC并发编程/image-20240731152649467.png

Classification of the four major memory barrier insertion strategies

rule,XXYY XX executes first, then YY executes

assets/JUC并发编程/image-20240730153728934.png StoreLoad prevents subsequent volatile read operations from being reordered, but also indirectly prevents volatile write operations (because volatile a write itself adds anotherStoreStore), and may indirectly prevent ordinary write reordering, or may not prevent it

assets/JUC并发编程/JUC并发编程-20250313213423698.png

The first operation and the second operation represent the order of precedence in the code

assets/JUC并发编程/image-20240730154300158.png

If reordering is not prohibited, then volatile ordinary read and write operations after a read operation may be reordered to before the volatile read operation, thereby breaking the happens-before relationship,causing program behavior to be unpredictable。 Ordinary read and write operations are very likely to depend on the volatile result of the read operation

assets/JUC并发编程/image-20240730154313185.png

When the second operation is a volatile write, no matter what the first operation is, they cannot be reordered. This operation ensures that operations before the volatile write are not reordered to after the volatile write

Although it is usually said that StoreLoad has the effect of preventing subsequent volatile read and write operations from being reordered to before the volatile write operation, in fact it also indirectly plays a role in preventing preceding ordinary read operations from being reordered to after the volatile write operation.

Even if no loadstore is added before the volatile write, the JVM can still guarantee program correctness in the following ways assets/JUC并发编程/JUC并发编程-20250313220141182.png

assets/JUC并发编程/image-20240730230231431.png

Operations after a read are not reordered to before the read Operations before a write are not reordered to after the write

volatile write: insert avolatile writebefore and after each operationStoreStotre/Storeloadbarrier volatile Readat eachvolatile Readinsert aLoadLoad,LoadStorebarrier after the operation

assets/JUC并发编程/image-20240730155435256.png assets/JUC并发编程/image-20240730155447112.png

volatile Write: at eachvolatile Writeinsert aStoreStotre/Storeloadbarrier before and after the operation
volatile Readat eachvolatile Readinsert aLoadLoad,LoadStorebarrier after the operation

Case

volatile int v = 0;
int a = 0;

void example() {
    v = a; // volatile Write
    a = 1; // Normal write
}

In this example, there are two operations:
1. `v = a;` is a `volatile` write operation.
2. `a = 1;` is a normal write operation.

According to the rules in the table:
1. `volatile` A normal write operation after a write operation can be reordered.
2. `volatile` After the write operation, `volatile` the write operation cannot be reordered.

**as-if-serial Semantics:** No matter how reordering is done (by the compiler and processor to improve parallelism), the execution result under a single thread must not be changed.
Happens-Before Application of the rules: Although a = 1; occurs v = a; after, according to data dependency, to obtain the correct v,actually a the assignment of happens-before v = a。
Summary:

- Although JMM allows `volatile` reordering of normal write operations after a write operation, when a data dependency exists,JMM prevents this reordering.

volatile Characteristics

JVM By itself it can only guarantee the atomicity of a single command. Multiple commands can only be guaranteed by locks.

volatile Cannot guarantee atomicity, can only guarantee visibility. Guaranteeing the atomicity of multiple commands depends on locks. The lock mechanism provides visibility and atomicity and can replace volatile

volotile Not suitable for participating in operations that depend on the current value

Visibility

volatile guarantees visibility

assets/JUC并发编程/image-20240730160705429.png Without addingvolatilethe program will not stop. The modifications made by the main thread also modify a copy of the variable and are not flushed to main memory. It is also possible that they are flushed to main memory, but another thread still uses the copy and does not obtain the latest value from main memory.

volatileRead and write process of the variable

assets/JUC并发编程/image-20240730171811625.png assets/JUC并发编程/image-20240730171819869.png assets/JUC并发编程/image-20240730193348187.png

No atomicity

Does not guarantee atomic operations

Can only guarantee visibility, reading and writing data directly from main memory, while multi threaded operations can still go wrong. Atomicity requireslock

such as i++ assets/JUC并发编程/image-20240730160321120.png assets/JUC并发编程/image-20240730184655305.png Correct result 10000, test 8177

According to the variable read and write process above,

assets/JUC并发编程/image-20240730193419295.png assets/JUC并发编程/image-20240730193434489.png assets/JUC并发编程/image-20240730193447727.png

  • Atomicity: focuses on one operation(may include multiple steps) whether it is executed as a whole, without interference from other threads.
  • Prohibit reordering (volatile): the focus is on the order between read and write operations of a single variable , as well as the order relative to other volatile variable read and write operations.

Instruction reordering prohibited

volatile

Prohibit operations before the write from being reordered to after the write operations from being reordered to volatile after (this means that before the volatile all write operations before the write operation have already completed and are visible to other threads), but operations after the write may be reordered, and if this has an effect then locking is needed

That is to say volatile read operations after operations will not be reordered before the read,write operations before operations have definitely completed, while lock can directly avoid all instruction reordering

volatile read after plus LoadStore and LoadLoad volatile write before plus StoreStore, after plus Storeload operations after the read will not be reordered before the read operations before the write will not be reordered after the write

volatileReordering prohibited

assets/JUC并发编程/image-20240731155507442.png assets/JUC并发编程/image-20240731155513507.png assets/JUC并发编程/image-20240731155524177.png assets/JUC并发编程/image-20240731155545392.png assets/JUC并发编程/image-20240731161639195.png assets/JUC并发编程/image-20240730154300158.png

assets/JUC并发编程/image-20240731162038037.png

operations before the write from being reordered to volatile after (this means that before the volatile all write operations before the write operation have alreadycompletedand are visible to other threads), but operations after the write may be reordered, and if this has an effect then locking is needed

In the Java memory model, ordinary write operations (such as y = 1) can be reordered to volatile before the write operation. This can indeed affect the volatile expected behavior of the variable, causing the value read in other threads to be inconsistent.

Example

int x = 0; volatile int v = 0; int y = 0;

Thread A x = 1; // ordinary write operation v = y; // volatile write operation y = 1; // ordinary write operation

Operation order in Thread A

In Thread A, we have three steps:

  1. x = 1; (ordinary write operation)
  2. v = y; (volatilewrite operation)
  3. y = 1; (ordinary write operation)

Reordering rules

According to the Java memory model,volatilewrite operations insert memory barriers before and after them:

  1. Store Barrier: before writing to thevolatilevariable, ensure that all previous ordinary write operations have completed and are visible to other threads.
  2. Load Barrier: after reading thevolatilevariable, ensure that all subsequent ordinary read operations read the latest value.

Regarding your question: can ordinary write operations be reordered before thevolatilewrite operation

In Thread A:

  • x = 1 is an ordinary write operation.
  • v = y is avolatilewrite operation.
  • y = 1 is an ordinary write operation.

According to the rules of the memory model:

  • volatileall ordinary write operations before the write operation cannot be reordered to after thevolatilewrite operation.
  • However,volatileordinary write operations after the write operation can be reordered to before thevolatilewrite operation.

Specific example

Consider the following code:

x = 1; // ordinary write operation v = y; // volatile write operation y = 1; // ordinary write operation

In this case, the following reordering may occur:

  • x = 1 and v = y order is fixed,x = 1 must be completed before v = y .
  • but y = 1 may be reordered to before v = y .

Therefore, the possible execution orders include:

  1. x = 1
  2. y = 1
  3. v = y

Impact on Thread B

For Thread B:

int r1 = v; // volatile read operation int r2 = x; // ordinary read operation int r3 = y; // ordinary read operation

Ifr1 = y, then according to the guarantee of the memory model,r2value must be1(becausex = 1is completed beforev = y). However,r3value is uncertain, becausey = 1may be beforev = y, or may be afterv = y.

Usage

use as a status flag

assets/JUC并发编程/image-20240731181850358.png

Read: volatile guarantees visibility Write: lock guarantees atomicity

A lower overhead read write lock strategy

Many reads, reduce overhead, no need to lock, only need to guarantee visibility

assets/JUC并发编程/image-20240731181902112.png

Multithreaded usage volatile DisablereorderingAvoid problems

Object creation is divided into three steps, and multithreading may issue reordering that causes errors, so you can use volatile Disable reordering Avoid the problem

DCL double ended locking

Double check

assets/JUC并发编程/image-20240731182843038.png

The second locking is because another thread may not have finished creation when the check starts, but when the lock is checked it has just finished creation and released the lock

BecauseSafeDoubleCheckSingletonnot addedvolatile singleton = new SafeDoubleCheckSingleton(); This line of code is actually divided into three steps:

  1. for SafeDoubleCheckSingleton allocate memory space.
  2. call SafeDoubleCheckSingleton constructor to initialize the object.
  3. set singleton reference to point to the allocated memory address.

Due to reordering, it may reach the second step while the object has not finished being created. Another thread judges that the object has already been created, causing an error. Therefore it is necessary tovolatileensure that reordering does not occur

assets/JUC并发编程/image-20240731190657168.png

Summary

Visibility assets/JUC并发编程/image-20240731192632872.png

Atomicity

Prohibit reordering

assets/JUC并发编程/image-20240731231332029.png

assets/JUC并发编程/image-20240731231409428.png

assets/JUC并发编程/image-20240731231350410.png

Why after writing volatile the system adds a memory barrier at the underlying level

assets/JUC并发编程/image-20240731221823179.png

CAS Compare and swap

The full name of CAS is Compare-And-Swap, and it is aCPU concurrency primitive. The execution of a primitive must be continuous and is not allowed to be interrupted during execution. That is to say, CAS is an atomic CPU instruction and will not cause so called data inconsistency problems

Its function is to determine whether the value at a certain memory location is the expected value. If so, change it to the new value. This process is atomic. It usesoptimisticlock retry mechanism

AtomicInteger The class mainly usesCAS (compare and swap) + volatileand native methods to ensure atomic operations, thereby avoiding synchronizedhigh overhead, and execution efficiency is greatly improved

withoutCASwhen : multiple threads need to add locks to ensure atomicity with CAS when : multiple threads useatomic classesto ensure atomicity

but there is theABA problem

What is CAS

assets/JUC并发编程/image-20240801124819665.png assets/JUC并发编程/image-20240801124912506.png If the main memory is 5 and the shared variable copy of multiple threads is 5, after updating to 6 and preparing to write to main memory, it will check whether the shared variable copy is still equal to main memory. If equal, it considers that no other thread has modified it. Otherwise it will not update and will reread the value from main memory

  • Guaranteed at the hardware level

assets/JUC并发编程/image-20240801130518272.png It uses hardware level instructions such as the cmpxchg instruction to ensure the atomicity of the compare and update operation. This can avoid the thread blocking and context switching overhead brought by traditional locking mechanisms, thereby improving the efficiency and response speed of the system.

assets/JUC并发编程/image-20240801132044055.png

Source code analysis

CAS is an CPU atomic instruction, a CPU concurrency primitive, and will not cause so called data inconsistency problems

Unsafe is the core class of CAS and can directly call the operating system underlying

Source code analysis

assets/JUC并发编程/image-20240801144030860.png assets/JUC并发编程/image-20240801144035086.png assets/JUC并发编程/image-20240801144046650.png assets/JUC并发编程/image-20240801144053650.png assets/JUC并发编程/image-20240801144243872.png assets/JUC并发编程/image-20240801144326601.png

Spin

A thread trying to acquire the lock will not block immediately, but instead usesloop methodto try to acquire the lock,

When a thread finds that the lock is occupied, it will continuously loop to determine the lock status until it is acquired. The advantage is reducing the consumption of thread context switching. The disadvantage is that the loop willconsume CPU

java

package com.atguigu.Interview.study.thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @auther zzyy
 * @create 2018-12-28 17:57
 * Problem: implement a spin lock
 * Spinlock benefit: the loop comparison acquires without blocking similar to wait blocking.
 *
 * Through CAS operation completing the spinlock,A thread first comes in and calls myLock method holds the lock by itself 5 seconds,B then comes in and finds
 * currently a thread holds the lock, not null,so can only wait by spinning until A after releasing the lock B then grabs it.
 */
public class SpinLockDemo
{
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock()
    {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"\t come in");
        while(!atomicReference.compareAndSet(null,thread))
        {

        }
    }

    public void myUnLock()
    {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(Thread.currentThread().getName()+"\t myUnLock over");
    }

    public static void main(String[] args)
    {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(() -> {
            spinLockDemo.myLock();
            try { TimeUnit.SECONDS.sleep( 5 ); } catch (InterruptedException e) { e.printStackTrace(); }
            spinLockDemo.myUnLock();
        },"A").start();


        //pause the thread for a while, ensuring A thread before B thread starts and completes
        try { TimeUnit.SECONDS.sleep( 1 ); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            spinLockDemo.myLock();
            spinLockDemo.myUnLock();
        },"B").start();

    }
}

CAS problems

  • Loop overhead may be large assets/JUC并发编程/image-20240801162129327.png
ABA problem

When thread A modifies data in memory, thread B comes and uses the data; after B changes the data, it restores the data to its original state. Thread A checks and thinks it has not changed, so the modification succeeds, resulting in a process safety problem assets/JUC并发编程/image-20240801164038389.png Therefore the version number mechanism is referenced, AtomicStampedReference assets/JUC并发编程/image-20240801163909953.png assets/JUC并发编程/image-20240801173730690.png

Atomic operation classes 18 Arhat enhancement

When the number of threads increases, the advantage of using atomic operation classes becomes more obvious

assets/JUC并发编程/image-20240801174406785.png assets/JUC并发编程/image-20240801174356799.png

Basic type atomic classes

AtomicInteger/AtomicBoolean/AtomicLong

class MyNumber
{
    @Getter
    private AtomicInteger atomicInteger = new AtomicInteger();//default is 0
    public void addPlusPlus()
    {
        atomicInteger.incrementAndGet();
    }
}

assets/JUC并发编程/image-20240801183702223.png

Java SE / 3. Application Development / Multithreading > CountDownLatch

Array type atomic classes

AtomiclntegerArray AtomicLongArray AtomicReferenceArray

assets/JUC并发编程/image-20240801190300630.png

Reference type atomic classes

AtomicReference AtomicStampedReference Version number AtomicMarkableReference

Atomic reference

new AtomicReference<>();

assets/JUC并发编程/image-20240801153957395.png

Convert the previously set z3 into li4

AtomicMarkableReference

AtomicMarkableReference, it allows you toatomically update a referenceand aboolean flag bit。

true or false change

assets/JUC并发编程/image-20240802003211363.png

assets/JUC并发编程/image-20240802003130179.png

Atomic classes for modifying object fields

Can in a thread safe way operatenon thread safe objectsome fields within

Field level atomic update

AtomicIntegerFieldUpdater In atomically updated objects int value of type field AtomicLongFieldUpdater In atomically updated objects Long value of type field AtomicReferenceFieldUpdater Atomic updatereference typevalue of field all must use public volatile keyword modifier

Because the atomic classes for modifying object properties are all abstract classes, each use must use a static method newUpdater() to create an updater, and you need to set the class and property you want to update.

Cannot avoid ABA, without addingsynchronized, ensure high performance atomicity

AtomicIntegerFieldUpdater

class BankAccount//resource class
{
    String bankName = "CCB";
    //The updated object property must use the public volatile modifier.
    public volatile int money = 0;//Amount of money
    public void add()
    {
        money++;
    }
    //Because the object property modification type atomic classes are all abstract classes, every use must
    //use the static method newUpdater()create an updater, and you need to set the class and property to be updated
    AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =
            AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");

    //do not add synchronized,guarantee high performance atomicity, local minimally invasive small surgery
    public void transMoney(BankAccount bankAccount)
    {
        fieldUpdater.getAndIncrement(bankAccount);
    }
}

/**
 * @auther zzyy
 * Operate some fields of a non thread safe object in a thread safe way.
 *
 * Requirement:
 * 10 threads,
 * each thread transfers money 1000,
 * do not use synchronized,try to use AtomicIntegerFieldUpdater to implement.
 */

public class AtomicIntegerFieldUpdaterDemo
{
    public static void main(String[] args) throws InterruptedException
    {
        BankAccount bankAccount = new BankAccount();
        CountDownLatch countDownLatch = new CountDownLatch(10);

        for (int i = 1; i <=10; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <=1000; j++) {
                        //bankAccount.add();
                        bankAccount.transMoney(bankAccount);
                    }
                } finally {
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName()+"\t"+"result: "+bankAccount.money);
    }
}

AtomicReferenceFieldUpdater.newUpdater(当前类,current parameter class, parameter name) Update a field in a class referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)

AtomicReferenceFieldUpdater

class MyVar //resource class
{
    public volatile Boolean isInit = Boolean.FALSE;

    AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater =
            AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");

    public void init(MyVar myVar)
    {
        if (referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE))
        {
            System.out.println(Thread.currentThread().getName()+"\t"+"----- start init,need 2 seconds");
            //pause the thread for a few seconds
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName()+"\t"+"----- over init");
        }else{
            System.out.println(Thread.currentThread().getName()+"\t"+"----- 已经有线程在进行初始化工作。。。。。");
        }
    }
}


/**
 * @auther zzyy
 * Requirement:
 * Multiple threads concurrently call the initialization method of a class, if it has not been initialized, the initialization work will be executed,
 * it is required that it can only be initialized once, and only one thread succeeds
 */
 
public class AtomicReferenceFieldUpdaterDemo
{
    public static void main(String[] args)
    {
        MyVar myVar = new MyVar();

        for (int i = 1; i <=5; i++) {
            new Thread(() -> {
                myVar.init(myVar);
            },String.valueOf(i)).start();
        }
    }
}

In depth analysis of the principle of atomic operation enhancement classes

Why it appeared and what it enhanced

assets/JUC并发编程/image-20240802224405044.png assets/JUC并发编程/image-20240802224558080.png assets/JUC并发编程/image-20240802224536756.png

DoubleAccumulator Create a new adder with an initial sum of 0, then carry out a transfer with each newly passed in number, depending on the overridden applyAsLong logic DoubleAdder LongAccumulator LongAdder

assets/JUC并发编程/image-20240803005418858.png

DoubleAdder, DoubleAccumulator and performance comparison

assets/JUC并发编程/image-20240802232939710.png

Comparison test, performance differs by 10 times assets/JUC并发编程/JUC并发编程-20250316181436884.png


import javax.lang.model.element.VariableElement;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

class ClickNumber //resource class
    //four calculation methods,test performance
{
    int number = 0;
    public synchronized void clickBySynchronized()
    {
        number++;
    }

    AtomicLong atomicLong = new AtomicLong(0);
    public void clickByAtomicLong()
    {
        atomicLong.getAndIncrement();
    }

    LongAdder longAdder = new LongAdder();
    public void clickByLongAdder()
    {
        longAdder.increment();
    }

    LongAccumulator longAccumulator = new LongAccumulator((x,y) -> x + y,0);
    public void clickByLongAccumulator()
    {
        longAccumulator.accumulate(1);
    }

}

/**
 * @auther zzyy
 * Requirement: 50 threads, each thread 100W times, the total number of likes comes out
 */
public class AccumulatorCompareDemo
{
    public static final int _1W = 10000;
    public static final int threadNumber = 50;

    public static void main(String[] args) throws InterruptedException
    {
        ClickNumber clickNumber = new ClickNumber();
        long startTime;
        long endTime;

        CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <=100 * _1W; j++) {
                        clickNumber.clickBySynchronized();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickBySynchronized: "+clickNumber.number);

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <=100 * _1W; j++) {
                        clickNumber.clickByAtomicLong();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByAtomicLong: "+clickNumber.atomicLong.get());


        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <=100 * _1W; j++) {
                        clickNumber.clickByLongAdder();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByLongAdder: "+clickNumber.longAdder.sum());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <=100 * _1W; j++) {
                        clickNumber.clickByLongAccumulator();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByLongAccumulator: "+clickNumber.longAccumulator.get());

    }
}

underlying source code

CAS only allows one thread to successfully modify the value of a shared variable, the remaining threads will fail and retry, when concurrency is high there will be very many retries, while LongAdder internally maintains a cell array based on CAS. Each cell independently maintains a count value. Each cell can have one thread enter and then update, multiple threads can operate on different cell, finally only need to accumulate each cell to get the result

Each thread ID is hashed, the hash maps to the index of some array cell, and then the value at that index is incremented

Why LongAdder is fast

LongAdderisStriped64subclass of

Striped64source code

assets/JUC并发编程/image-20240803120050342.png

CASonly allows one thread to successfully modify the value of a shared variable, the remaining threads will fail and retry, when concurrency is high there will be very many retries, whileLongAdderinternally maintains aCASbasedcellarray. Eachcellindependently maintains a count value. Eachcellcan have one thread enter and then update, multiple threads can operate on differentcell, finally only need to accumulate eachcellto get the result assets/JUC并发编程/image-20240803141847820.png LongAdderequals base variable plus Cell[]array assets/JUC并发编程/image-20240803123511690.png

assets/JUC并发编程/image-20240803123458955.png

assets/JUC并发编程/image-20240803123728656.png

assets/JUC并发编程/image-20240803135335269.png assets/JUC并发编程/image-20240803145927792.png

https://www.bilibili.com/video/BV1ar4y1x727/?p=90&vd_source=17535a06cd4587318e9b67ac64afeab7

92 to 99 not read

LongAddersource code

assets/JUC并发编程/image-20240803154305457.png (as equals cells) not equal to null : check whether the cells array has been initialized, initialized toture if not initialized then proceed,!casBase(b = base, b + x), try to use CAS operations to directly update the base counter base。

assets/JUC并发编程/image-20240803153138816.png

one base, then create two cells

Lock -CAS-Volatile Summary

JMM: visibility, atomicity, ordering (instruction reordering)

Visibility Atomicity Ordering
Volatile √ × Partial
CAS × √ ×
Volatile plus CAS √ √ Partial
Lock √ √ √

Locking, even if the operation is on an ordinary, non volatile variable, can also guarantee visibility, atomicity, and ordering. And, in this case, indeedno needto additionally use the volatile keyword

  • Volatile

Volatile provides visibility, partial ordering, does not guarantee atomicity

after a readoperations are not reordered before the read before a writeoperations are not reordered after the write

Read : volitile guarantees visibility Write : lock guarantees atomicity

cannot guarantee ordering between multiple volatile variables

volatile int x = 0;
volatile int y = 0;

// thread 1
x = 1;
y = 2;

// thread 2
System.out.println("x = " + x + ", y = " + y);

volatile int a = 0;
int b = 0;

//thread 1
a = 1;
b = 2;

//thread 2
if (b == 2) {
    System.out.println(a); // may output 0,may also output 1
}
  • CAS Compare-And-Swap

various atomic classes, using an optimistic locking mechanism ensure the atomicity of the compare and update operation through hardware level instructions (such as cmpxchg instructions) usually combined with volatile to achieve visibility, and partial ordering

assets/JUC并发编程/image-20240822130345742.png

Without CAS : multithreading needs to add locks to guarantee atomicity With CAS : multithreading uses atomic classes can guarantee atomicity

  • Lockbiased lock-lightweight-heavyweight

A lock can directly guarantee visibility, atomicity, ordering , no need forvolatile

Usually, if there are many concurrent read and write operations and the contention is not fierce, consider using CAS plus volatile; if stronger thread safety is needed or the contention is relatively fierce, you can choose to use locks directly.

ThreadLocal : Provides a kind of thread local variable, each thread has its own copy, and there is no longer any need to use Voliatile/CAS/locks

ThreadLocal

ThreadLocal to providethread local variables. These variables differ from normal variables because each thread, when accessing the ThreadLocal instance (through its get or set method), has its own independently initialized copy of the variable.ThreadLocal The instance is usually a private static field in a class, and the purpose of using it is to associate state (for example, a user ID or transaction ID) with a thread.

Achieve that each thread has its own dedicated local variable copy (each uses its own variable without troubling others, without sharing with others, everyone has a share, each person has one copy)

Provides a kind of thread local variable, each thread has its own copy, and there is no longer any need to use Voliatile/CAS/locks

Usage method

assets/JUC并发编程/image-20240803222847750.png

  • Initialization method initialValue/withInitial The two methods are consistent withInitial It is a new method, more concise than the former

initialValue Initialization is complex

private static final ThreadLocal<Integer> saleVolume = new ThreadLocal<>() {
    @Override
    protected Integer initialValue() {
        return 0;
    }
};

public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();Direct initialization is not recommended. Default is null May throw a null pointer exception

It is recommended to use withInitial initialization, becauseThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);it only needs to be initialized once, and afterward each thread uses it with its own copy initial value, and afterward this data can be operated on, but each thread can only have one copy, and after each thread is done using its own it needs to use remove() to perform cleanup

After use, the custom ThreadLocal variable needs to be cleaned up, usually usingtry-catch-finally

It is not about ThreadLocal implemented ThreadLocal, but about ThreadLocalMap, each thread operating ThreadLocal in the set method is bound to its own current thread, so ThreadLocal it only needs to be initialized once, and only one block of space needs to be allocated, so it is often used static to modify

assets/JUC并发编程/image-20240804123057398.png

assets/JUC并发编程/image-20240803223100513.png

java

Case test where each thread records its own data


import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

class House //Resource class
{
    int saleCount = 0;
    public synchronized void saleHouse()
    {
        ++saleCount;
    }

    ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
    public void saleVolumeByThreadLocal()
    {
        saleVolume.set(1+saleVolume.get());
    }
}

/**
 * @auther zzyy
 * @create 2021-12-31 15:46
 * Requirement 1: 5 salespeople sell houses, and the group executives only care about the accurate statistical count of total sales.
 * Requirement 2: 5 each salesperson sells a random number of houses, each with an independent sales amount, their own performance follows commissions, each eats from their own pot, each salesperson works on their own, self reliant and well fed
 */

public class ThreadLocalDemo
{
    public static void main(String[] args) throws InterruptedException
    {

        House house = new House();

        for (int i = 1; i <=5; i++) {
            new Thread(() -> {
                int size = new Random().nextInt(5)+1;
                try {
                    for (int j = 1; j <=size; j++) {
                        house.saleHouse();
                        house.saleVolumeByThreadLocal();
                    }
                    System.out.println(Thread.currentThread().getName()+"\t"+"号销售卖出:"+house.saleVolume.get());
                } finally {
                    house.saleVolume.remove();
                }
            },String.valueOf(i)).start();
        };

        //Pause milliseconds
        try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }

        System.out.println(Thread.currentThread().getName()+"\t"+"共计卖出多少套: "+house.saleCount);
    }
}

Source code analysis

Thread--> ThreadLocal -->ThreadLocalMap-->Current thread and current variable

Thread Contains ThreadLocal ThreadLocal Contains static ThreadLocalMap Class ThreadLocalMap Contains storage for the current thread and current variable

What multiple threads can access at the same time is actually the same ThreadLocal instance, but each thread has its own ThreadLocalMap object (threadLocals) Each thread has its own Threadlocals variable, which is a ThreadlocalMap collection, and ThreadLocal helps us put data into our own Threadlocals

Similar to the relationship of person to card to card information assets/JUC并发编程/image-20240804154343938.png assets/JUC并发编程/image-20240804161428479.png assets/JUC并发编程/image-20240822195901488.png

Memory leak problem

ThreadLocalMap It is wrapped in two layers ThreadLocal Object

(1) The first layer of wrapping uses WeakReference<ThreadLocal<?>>to turn theThreadLocalobject into a weakly referenced object (blue box) (2) The second layer of wrapping defines a dedicated class Entry to extendWeakReference<ThreadLocal<?>>

assets/JUC并发编程/image-20240804173652406.png assets/JUC并发编程/image-20240804174507631.png assets/JUC并发编程/image-20240804174641475.png

JVM memory and garbage collection > Revisiting References

A thread can use multiple ThreadLocal variables, so the key uses ThreadLocal,to distinguish the different thread-local variables of the same thread ThreadLocalMap stored in

Reference Review

Soft Reference It is usually used in memory-sensitive programs, for example caches use soft references. When memory is sufficient, the object is retained; when memory is insufficient, it is collected. assets/JUC并发编程/image-20240804180530352.png

Weak Reference: collected as soon as discovered assets/JUC并发编程/image-20240804180804388.png

Phantom Reference: As the name implies, it exists in name only. Unlike the other kinds of references, a phantom reference does not determine the lifecycle of the object. assets/JUC并发编程/image-20240804180707056.png The only purpose of setting a phantom reference association is to receive a system notification when the object is collected by the garbage collector, or to add further processing later.

After the thread finishes running Threadlocal it should be collected, while ThreadLocalMap stores ThreadLocal the value, that is, it references ThreadLocal , if it is a strong reference, it will cause Threadlocal to not be collected, causing a memory leak

Entry Weak reference ThreadLocal

Entry extends WeakReference<ThreadLocal<?>>

Therefore it needs to be designed as a weak reference, but at this point it is still incomplete After designing it as a weak reference, ThreadLocalafter it is collected Entry the key will point to null, whilevalue the value is a strong referenceObject value;, and because it is set to null afterwards the key-value pair cannot be accessed, causing a memory leak, so a weak reference cannot 100% guarantee no memory leak, so manual cleanup is needed remove, set/get It will also check all keys that are null the Entry objects

Of course, when the thread ends Thread stopping will be collected, but the thread pool reuses threads, so the thread will not stop. In this case the leak needs attention

Class A weakly references class B : A extends WeakReference<B>

Strong reference chain Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value

Why the source code uses weak references

assets/JUC并发编程/image-20240804173652406.png Entry It extends WeakReference<ThreadLocal<?>>, which means it holds a ThreadLocal<?> weak reference to the object

assets/JUC并发编程/image-20240804182844169.png assets/JUC并发编程/image-20240804184646569.png After collection using a weak reference Entry the key will point to null

Because in thread pool scenarios, the thread pool is often reused Object value; At this point, using only weak references means there will be many entries with a null key, but the value always exists, which will also trigger a new memory leak, so remove is needed for manual cleanup

The main reason the value is a strong reference is to ensure that the value of the thread-local variable can be accessed normally while the key exists. If the value were also a weak reference, then during garbage collection, even if the key still exists, the value might be collected, which would cause unexpected behavior.

assets/JUC并发编程/image-20240804185058817.png assets/JUC并发编程/image-20240804185902776.png assets/JUC并发编程/image-20240804193323577.png assets/JUC并发编程/image-20240804193339617.png

The set and get methods will check all Entry objects whose key is null assets/JUC并发编程/image-20240805161352947.png assets/JUC并发编程/image-20240805161403400.png assets/JUC并发编程/image-20240805161416361.png assets/JUC并发编程/image-20240805161424677.png

Alibaba

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.11.2</version>
</dependency>

Alibaba open source TransmittableThreadLocal (TTL) is an enhanced version of ThreadLocal, it can pass the parent threadcontext。

Allows the value set by the parent thread to be passed to child threads, solving the data passing problem of ThreadLocal in a thread pool environment.

Summary

pp; pp assets/JUC并发编程/image-20240805164051846.png

ThreadLocal Significance of its appearance:

Multithreading: gives each thread its own exclusive space, reducing the use of locks Single thread: such as storing current session or transaction information, passing information within the current thread assets/JUC并发编程/image-20240822190019246.png Single thread usage case ../../实战项目/assets/苍穹外卖/image-20231029165406992.png

If set tostaticthen every thread accesses the sameThreadLocalinstance Generally also onlynewoneThreadLocalinstance, and in multithreaded use it is still a variable exclusive to each thread

Single thread usage scenario: when during the running of a thread, data used at the beginning is needed again later, it can be stored withThreadLocalfor later use by that thread

ThreadLocal and static variables can to some extent be seen as opposite forms:

Summary:

  • ThreadLocal The variable is private to the thread, and each thread has its own copy.

  • static The variable is globally shared, and all threads share the same instance.

  • remove When ThreadLocal is no longer used, while ThreadLocalMap still stores ThreadLocal which prevents collection, so it is designed as a weak reference ThreadLocal, allowing ThreadLocal to be collected normally, but ThreadlocalMap the key value pair in it can no longer be accessed, causing the value to be unable to be collected, so manual remove, set/get also checks all keys that are null of the Entry object

javabetween multiple objects, strong and weak reference relationships are usually designed

Java memory layout and object header

JVM memory and garbage collection > Object instantiation, memory layout and access positioning

../JVM/assets/上内存与垃圾回收/image-20240113111239156.jpeg assets/JUC并发编程/image-20240805180824838.png

  1. Object header (16 bytes)
    1. Object mark Mark word (8 bytes)
    2. Type pointer (8 bytes) assets/JUC并发编程/image-20240805174002599.png Mark word 8 bytes, the 64 bit distribution assets/JUC并发编程/image-20240805174024577.png Type pointer is the pointer to the class template file in the method area, in order to determine which class the instance belongs to Memory alignment
  2. Instance data: the attribute information of the class, the attribute information of the parent class, and if it is an array instance part, including the array length, aligned to 4 bytes
  3. Alignment padding: the virtual machine requires that the starting address of an object must be an integer multiple of 8 bytes. The padding data does not have to exist, it is only for byte alignment, and this part of memory is padded and aligned by 8 bytes.

Mark word (8 bytes) is 64bit

assets/JUC并发编程/image-20240805211131947.png

64bit various combinations can show whether there is a lock and what kind of lock is added

assets/JUC并发编程/image-20240805210928447.png the last two bits represent different lock states

assets/JUC并发编程/image-20240806115250551.png 4bit can represent 0 to 15 the numerical generational age between them is represented by 4bit so it cannot support changing to 16 assets/JUC并发编程/image-20240806115240483.png

java

assets/JUC并发编程/image-20240805215207896.png assets/JUC并发编程/image-20240806115430261.png assets/JUC并发编程/image-20240806115446412.png the type pointer is compressed, from 8 to 4 -XX:+UseCompressedClassPointers


<!--
Official website:http://openjdk.java.net/projects/code-tools/jol/
Purpose: analyze the object in JVM the size and distribution
-->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>


ackage com.atguigu.juc.senior.inner.object;
import org.openjdk.jol.vm.VM;
/**
 * @auther zzyy
 * @create 2020-06-13 11:24
 */
public class MyObject
{
    public static void main(String[] args){
        //VM the detailed situation of the details
        System.out.println(VM.current().details());
        //the bytes allocated for all objects are 8 an integer multiple of.
        System.out.println(VM.current().objectAlignment());
    }
}

Sychronized and lock upgrade

assets/JUC并发编程/image-20240806145512914.png

Performance changes

java threads are mapped onto native operating system threads, requiring switching between user mode and kernel mode, which consumes a large amount of system resources

Kernel mode and user mode

User mode and kernel mode are terms describing different privilege levels in an operating system. User mode runs at a lower privilege level and is mainly used for ordinary applications, restricting direct access to hardware and system resources. Kernel mode runs at the highest privilege level and allows direct access to and control of all hardware and system resources, and is mainly used for the operating system kernel and critical system services.

assets/JUC并发编程/image-20240823160622326.png assets/JUC并发编程/image-20240823160602009.png

In early versions,synchronized they are heavyweight locks, with low efficiency, because the monitor lock (monitor) depends on the underlying operating system Mutex Lock for its implementation Java 6 later, in order to reduce the performance overhead caused by acquiring and releasing locks, lightweight locks and biased locks were introduced

Introduction

assets/JUC并发编程/image-20240806143944206.png assets/JUC并发编程/image-20240806143954808.png assets/JUC并发编程/image-20240806144455667.png

Mutex Lock assets/JUC并发编程/image-20240806145426252.png

Overview diagram

assets/JUC并发编程/image-20240806145621368.png assets/JUC并发编程/image-20240806145658941.png

Lock types and upgrade steps

In the object header Mark Word they are reused according to differences in the lock flag bits, along with the lock upgrade strategy

No lock

No lock

No lock: the initial state; after an object is instantiated, if it is not contended for by any thread, then it is in the no lock state

assets/JUC并发编程/image-20240806115446412.png 0 4 of 01 represents no lock

Although the object has been created, it has not called hashcode it will not record hash encoded

Look from the bottom right corner upward unused assets/JUC并发编程/image-20240806171505230.png

https://www.bilibili.com/video/BV1ar4y1x727?p=124&vd_source=17535a06cd4587318e9b67ac64afeab7

The last eight bits assets/JUC并发编程/image-20240806171606932.png assets/JUC并发编程/image-20240806171613125.png

In the object header

assets/JUC并发编程/image-20240805210928447.png

Biased lock

enabled by default, but with a delay4s, and during the delay it may skip the biased lock and add a lightweight lock

In actual operation, it often happens that one thread acquires a lock many times, so in the future period it is very likely to be locked again by the same thread. Therefore, in order to reduce the overhead of acquiring the lock, the JVM stores lock information in the object header to record the ID of the thread currently holding the lock, so that when acquiring the lock in the future it can quickly determine whether it is the same thread acquiring the lock, and that thread will automatically obtain the lock on subsequent accesses

JVM Use CAS operation tothread pointer ID record it into Mark Word , and modify the bias flag to indicate that the current thread obtains the lock. The lock object becomes a biased lock (through CAS modifying the lock flag bits in the object header), literally meaning a lock that is biased toward the first thread that acquires it. After the synchronized code block finishes executing, the thread does not actively release the biased lock.

That thread no longer needs to Monitor compete for the object, while other threads still need to.

Thread scheduling is controlled by the operating system, and the execution order of threads cannot be precisely controlled, so a situation may occur where a certain thread acquires the lock multiple times.

Introduction

assets/JUC并发编程/image-20240806183041173.png assets/JUC并发编程/image-20240806185745623.png assets/JUC并发编程/image-20240806185759909.png assets/JUC并发编程/image-20240806184707495.png assets/JUC并发编程/image-20240806185722224.png assets/JUC并发编程/image-20240806185726690.png

Biased lock test

enabled by default, but with a 4 second delay, and during the delay it may skip the biased lock and add a lightweight lock

java -XX:+PrintFlagsInitial | grep BiasedLock* assets/JUC并发编程/image-20240806190426488.png

assets/JUC并发编程/image-20240806190415086.png

or wait 5 seconds, when the biased lock is enabled assets/JUC并发编程/image-20240806190511629.png

Lightweight lock

java15 biased locks will gradually be deprecated later, due to high overhead and cost

assets/JUC并发编程/image-20240807205908770.png

Essentially it is a spin lock CAS spin first and then block; when a thread comes to participate in lock contention, but the lock acquisition conflict time is very short

The lightweight lock is used when threadsnearly alternateexecuting synchronized blocks to improve performance

Once another thread contends and finds that the current thread ID stored in the Mark Word is not its own, it will use CAS to try to acquire the lock; if it succeeds, it replaces the ID in the Mark Word with its own, and if it fails, it spins and waits, and the biased lock upgrades to a lightweight lock

assets/JUC并发编程/image-20240807205110422.png assets/JUC并发编程/image-20240807214843898.png When acquiring the biased lock fails, it will upgrade to a lightweight lock, JVM it will create for each thread in the current thread stack frame a space used to store the lock record (in the JVM stack), officially known as Displaced Mark WordIf a thread acquires the lock and finds that it is a lightweight lock, it will MarkWord copy the lock to its own Displaced Mark Word. inside. Then the thread tries to use CAS to change the lock MarkWord replace with pointer to the lock record. If it succeeds, the current thread acquires the lock. If it fails, it means Mark Word has already been replaced by another thread lock record, which means it is competing with other threads for the lock. The current thread then tries to use spinning to acquire the lock.

Ensure fair competition for locks among threads

Revocation of biased locks
  1. After this thread finishes executing
  2. Another thread tries to seize it

When another thread gradually comes to compete for the lock, the biased lock can no longer be used and must be upgraded to a lightweight lock

The competing thread tries CAS to update the object header. If it fails, it waits until a global safepoint (no code is executed at this time) to revoke the biased lock.

assets/JUC并发编程/image-20240807091419806.png

assets/JUC并发编程/image-20240807091651181.png assets/JUC并发编程/image-20240807091703597.png assets/JUC并发编程/image-20240807092515137.png assets/JUC并发编程/image-20240807092532804.png

Heavyweight lock

Spin count: Adaptive spinning is an optimization technique that, based on the current thread performance during its previous spin attempt to acquire the lock,dynamically adjuststhe number of spins for the next time. Then it upgrades to a heavyweight lock

In most cases, a lightweight lock is used instead of a heavyweight lock

assets/JUC并发编程/image-20240807222404057.png

Summary

In one sentence: spin first, and if that does not work, use a heavyweight lock

  • biased lock biased lock: Mark Word stores the current thread ID (delay 4s) In this way, when acquiring the lock in the future, it can quickly determine whether the same thread is acquiring the lock, and the thread will automatically acquire the lock on subsequent accesses After a biased lock is acquired, it is not actively released. If acquiring the biased lock fails, it upgrades to a lightweight lock This is to improve performance when only one thread executes synchronization. Modern multi-core processors often compete, so biased locks were abolished

  • biased lock to lightweight lock Once another thread competes and finds that Mark Word the thread ID stored in it is not its own, it will use CAS to try to acquire the lock. If it succeeds, it is still a biased lock. If it fails, it waits until a global safepoint. The thread holding the biased lock revokes the original biased lock, upgrades to a lightweight lock, and copies the lock MarkWord to its own Displaced Mark Word. inside. Then the thread tries to use CAS to change the lock MarkWord replace with pointer to the lock record。 biased lock and lightweight lock Both do not involve switching from kernel mode to user mode

  • lightweight lock lightweight lock spin lock CAS, a thread participates in lock competition, but the lock acquisition conflict time is extremely short, the lightweight lock is for when threads nearly alternate executing synchronized blocks to improve performance

  • to heavyweight lock Under multi-threaded competition, a biased lock may skip the lightweight lock and directly upgrade to a heavyweight lock or a lightweight lock directly upgrades to a heavyweight lock

  1. Multiple threads frequently compete for the same lock: When multiple threads frequently try to acquire the same lock, the biased lock is revoked and directly upgraded to a heavyweight lock.
  2. Thread count exceeds the threshold: The JVM has a threshold. When the number of threads trying to acquire the lock exceeds this threshold, the biased lock directly upgrades to a heavyweight lock.

JVMThis threshold is dynamically adjusted based on the intensity of thread competition and system load. Once exceeded, it upgrades

assets/JUC并发编程/image-20240808001958197.png

Where does the hash code go after lock upgrade?

Biased locks cannot coexist with hash codes If the hash code is calculated before using the lock, it directly upgrades to a lightweight lock If the hash code is calculated while using the lock, it will directly upgrade to a heavyweight lock

Lightweight locks can coexist assets/JUC并发编程/image-20240807231547249.png

Once an object has calculated its hash code, it cannot enter the lock, otherwise it will cause encoding confusion o.hashCode() assets/JUC并发编程/image-20240807223214025.png assets/JUC并发编程/image-20240807223224017.png assets/JUC并发编程/image-20240807231320446.png assets/JUC并发编程/image-20240807232722348.png

Advantages and disadvantages of various locks

assets/JUC并发编程/image-20240807234747635.png

Lock elimination: JIT will ignore locks that do not spread shared usage to other threads

Lock coarsening: within a certain range it is all one lock, applying for one lock is enough, the compiler will perform optimization

JIT optimization of locks

Lock elimination assets/JUC并发编程/image-20240808000541721.png assets/JUC并发编程/image-20240808000500958.png

Lock coarsening assets/JUC并发编程/image-20240808000531269.png assets/JUC并发编程/image-20240808000520410.png

AbstractQueuedSynchronizer of AQS

An abstract queue synchronizer, many have not grabbed the lock, at this time a queue is needed to manage these threads that still need to grab the lock

It is used to build the heavyweight basic framework of locks or other synchronizer components and the entire JUC cornerstone of the system, through the built in FFO queue to complete the queuing work of threads acquiring resources, and through aint class variable represents the state of holding the lock

AbstractQueuedSynchronizer abbreviated as AQS

assets/JUC并发编程/image-20240809192711591.png assets/JUC并发编程/image-20240810000149882.png assets/JUC并发编程/image-20240810000219803.png

Internal structure

assets/JUC并发编程/image-20240812174131887.png

assets/JUC并发编程/image-20240812205144017.png

assets/JUC并发编程/image-20240812182638121.png Blocking enqueue

assets/JUC并发编程/image-20240812182626469.png

assets/JUC并发编程/image-20240812213413414.png

ReentrantLock

ReentrantLock It is the operation sync, externally retained FairSync/NofairSync

lock-->acquire-->tryAcquire-->addWaiter-->acquireQueued

assets/JUC并发编程/image-20240814003145227.png Assume A needs a particularly long time, explain under this circumstance

Enqueue

lock

assets/JUC并发编程/image-20240812220109122.png assets/JUC并发编程/image-20240812223841754.png

  1. lock The non fair one directly preempts with CAS, if it fails to grab it entersacquire The fair one entersacquire

assets/JUC并发编程/image-20240812223849067.png assets/JUC并发编程/image-20240812224702621.png

acquire
  1. acquireThere is no actual method, the parameter is of int type, representing the reusability of the lock

Call tryAcquire acquire is the parent classAbstractQueuedSynchronizerof, both classes are the same, it will call the overridden one of the subclasstryacquire

assets/JUC并发编程/image-20240812225316742.png

tryAcquire
  1. tryAcquire(int)to grab the lock

The fair lock has one morehasQueuedPredecessors(), everything else is the same getstatueis 1, indicating another thread is occupying it

In the non fair lock else if (current == getExclusiveOwnerThread()) Guarantee the reusability of the lock

assets/JUC并发编程/image-20240812220129562.png assets/JUC并发编程/image-20240812220118289.png

assets/JUC并发编程/image-20240812224725010.png

The only difference between the two locks is that the fair lock has one more restriction when acquiring the synchronization state: hasQueuedPredecessors() assets/JUC并发编程/image-20240812220133497.png

addWaiter

assets/JUC并发编程/image-20240812225316742.png

  1. indicating the above lock grabbing failed, entering waitingaddWaiter(Node.EXCLUSIVE)Create Node and join the waiting queue

modeThere are two kinds: SHAREDShared waiting lock EXCLUSIVEExclusive waiting lock

tailInitially it isnull

assets/JUC并发编程/image-20240813152543390.png

then it directly entersenq, it will only run once, performing initialization enq The method only on the first addition node will be called, the second time it will not be called

assets/JUC并发编程/image-20240813232005592.png

tis the previous tail node.prev=t The previous node of the current node points to the previous tail compareAndSetTail(pred, node)Update the tail, set it to the current node t.nextMake the previous node point to the current node The purpose of the upper half of the code is to initialize and create an empty node, and point both the head node and the tail node to this newly created node.

assets/JUC并发编程/image-20240813153927886.png Then t is no longer null, but the first virtual node, so it does not enter the first if, and enters the else This piece of code sets the first node to enter the queue as the tail node

assets/JUC并发编程/image-20240813194420792.png

How does it wake up, queue, and stay stable in the queue

acquireQueued
  1. acquireQueued(node, arg) Blocks the current thread in the waiting queue until it successfully acquires the synchronization state.

assets/JUC并发编程/image-20240813234052960.png

head is initially the initial default empty node, afterwards they are all the node currently using the lock The previous one is head, indicating it is now its own turn and then it continues tryAcquire, assuming other threads are still processing now, then it does not enter

failed The variable exists to ensure that under any exception or interruption, resources can be properly released and cleaned up.

assets/JUC并发编程/image-20240814094651109.png

Then enter shouldParkAfterFailedAcquire regarding the state change

the initial value of the previous node waitStatus default 0, then enter compareAndSetWaitStatus(pred, ws, Node.SIGNAL); Node. SIGNAL to -1, changed to -1, returns flase and it jumps out, enters the next loop, and enters again shouldParkAfterFailedAcquire regarding the state change, the second entry is -1 returned true, then LockSupport.park(this); , this is the current node, blocking was performed, it will wait for the pass, this is AQS how the underlying layer makes the node stop here

Then node C enters, likewise waitStatus, the first time 0 returns false then changed to -1 returnsture, likewise set park(this)

assets/JUC并发编程/image-20240814002243719.png

that is, the following nodes will all change the previous waitStatus to -1, then block itself

only the one after the virtual node node or the next node of the node currently using the lock and preempts tryacquire only if successful will it enter the blue

the nodes further back in the queue will all enter the red

assets/JUC并发编程/image-20240814100942757.png

assets/JUC并发编程/image-20240814000519279.png assets/JUC并发编程/image-20240814001414610.png assets/JUC并发编程/image-20240814001604251.png

At this point B is stuck here spinning

dequeue

unlock-->sync.release(1)-->tryRelease(arg)-->unparkSuccessor

sync.release(1)

assets/JUC并发编程/image-20240814004536401.png assets/JUC并发编程/image-20240814004554941.png

tryRelease(arg)

assets/JUC并发编程/image-20240814004828925.png Thread.currentThread() != getExclusiveOwnerThread() Ensure that the thread currently calling the unlock method is the thread holding the lock. If the current thread is not the thread holding the lock, then throw IllegalMonitorStateException an exception. Prevent other threads from attempting to release the lock without holding it, ensuring the correctness and consistency of the lock.

state is 1, minus 1 becomes 0, then release that thread

then tryRelease(arg) has finished running, returns ture

assets/JUC并发编程/image-20240814004554941.png h != null the head node is definitely notnull h.waitStatus != 0 the head node waitStatus, as long as another thread comes to compete, it will definitely also not equal 0

unparkSuccessor

then call unparkSuccessor(h) pass inhead again the virtual node waitStatus the previous one in -1 set to 0

assets/JUC并发编程/image-20240814010556372.png

at this time s is the virtual node next node

the first if,S==null is false does not enter the second enters

if (s != null)  
    LockSupport.unpark(s.thread);

released the pass after releasing the pass, parkAndCheckInterrupt, what is returned is still flase, but in for (;;) , at this time tryAcquire Assume it succeeds, it can smoothly enter the blue (A non-fair lock does not follow first-in first-out in the queue, it may be directly grabbed by another)

assets/JUC并发编程/image-20240814001414610.png assets/JUC并发编程/image-20240814002741932.png

setHead

Set the current latest node to head node.thread = null; node.prev = null; assets/JUC并发编程/image-20240814095625089.png After node successfully acquires the lock, the thread variable is no longer needed. At this point, node has become the head node of the queue, and the thread information no longer needs to be kept in the node. By setting the thread field to null, it helps the garbage collector reclaim objects that are no longer needed, thereby avoiding memory leaks. assets/JUC并发编程/image-20240814095917970.png assets/JUC并发编程/image-20240814100143058.png After acquiring, the thread is no longer needed assets/JUC并发编程/image-20240814100314540.png

p.next set to nullso that the next one pointed to by the previous node is set to nullat this point it can be garbage collected

assets/JUC并发编程/image-20240814094608188.png assets/JUC并发编程/image-20240814100609468.png

Cancel flow

assets/JUC并发编程/image-20240814102201023.png

assets/JUC并发编程/image-20240814164324379.png assets/JUC并发编程/image-20240814164347508.png

This is usually used to skip canceled nodes and ensure the predecessor node of the current node is a valid, uncanceled node.
take node of prev of prev assign to the current node.prev,until finding the previous node that is not canceled

while (pred.waitStatus > 0)  
    node.prev = pred = pred.prev;

Summary

Node

Each node node contains previous and next information, the queue contains head/tail information, thus forming a queue assets/JUC并发编程/image-20240814095244600.png assets/JUC并发编程/image-20240814094608188.png assets/JUC并发编程/image-20240812204421348.png

Introduction

assets/JUC并发编程/image-20240812204208264.png assets/JUC并发编程/image-20240812204227788.png

StampedLock

Java SE, 3. Application development, multithreading > ReentrantLock

ReentrantLock-->ReentranReadWriteLock-->StampedLock

ReentranReadWriteLock When there are many reads and few writes, there may bewrite lockstarvation, the situation of being unable to grab the lock

which led to stamped lock StampedLock

StampedLock After adopting optimistic lock acquisition, other threads trying to acquire the write lock will not be blocked. This is actually an optimization for the read lock, so after acquiring the optimistic read lock, the result still needs to be verified. assets/JUC并发编程/image-20240817173505236.png

assets/JUC并发编程/image-20240817174023103.png stamped lock StampedLock it allows the write lock to intervene during the reading process, avoiding write lock starvation assets/JUC并发编程/image-20240817174519401.png

static StampedLock stampedLock = new StampedLock(); long stamp = stampedLock.tryOptimisticRead(); stampedLock.validate(stamp) Check whether it has changed,false represents there is a modification stampedLock.readLock() same as the read-write lock stampedLock.writeLock() same as the read-write lock

note

assets/JUC并发编程/image-20240817182159170.png

  • Disadvantages

StampedLock Does not support reentrancy, does not start with Re StampedLock The pessimistic read lock and write lock do not support condition variables (Condition), this also needs attention. Use StampedLock Be sure not to call the interrupt operation, that is, do not call interrupt()method

Thinking

  • In multithreading: between the execution of any two lines of code, another thread may have executed other code
  • What it is, what it can do, how it came about, how it was done before it existed, which pain points it solved, which places it made convenient