当前位置:网站首页>JUC synchronization
JUC synchronization
2022-07-22 13:49:00 【Learn from Tao Ge】
A critical region
Critical resources : Resources that can only be used by one process at a time become critical resources
A critical region : Code block to access critical resources
Race condition : Multiple threads execute in a critical area , The results are unpredictable due to the different execution sequences of the code , It is called a race condition
There is no problem for a program to run multiple threads , There is no problem for multiple threads to read shared resources , Instruction interleaving occurs when multiple threads read and write to shared resources , There will be problems
In order to avoid the occurrence of race conditions in the critical region ( Solving thread safety problems ):
- Blocking solutions :synchronized,lock
- Non blocking solution : Atomic variable
Tube side (monitor): The software module is composed of several public variables local to itself and all the processes of accessing these public variables , Ensure that only one process is active in the pipeline at the same time , That is, the operation defined in the process is called by only one process at the same time ( Implemented by compiler )
synchronized: Object lock , The atomicity of the code in the critical region is guaranteed , At most one thread can hold the object lock at the same time by mutual exclusion , When other threads acquire this object lock, they will block , Ensure that the thread with the lock can safely execute the code in the critical area , Don't worry about thread context switching
Mutual exclusion and synchronization can be used synchronized Keyword to complete , difference :
- Mutual exclusion is to ensure that the race condition in the critical region occurs , Only one thread can execute critical area code at a time
- Synchronization is due to the sequence of thread execution 、 Different order 、 A thread needs to wait for other threads to run to a certain point
performance :
- Thread safety , Poor performance
- Thread unsafe, good performance , If there is no multithreading safety problem in development , It is recommended to use thread unsafe design classes
syn-ed
Using locks
Synchronized block
Lock object : In theory, it can be Any unique object
synchronized It's reentrant 、 Unfair heavyweight lock
In principle, :
- Lock objects are recommended to use shared resources
- Use... In the instance method this As lock object , Locked up this It happens to be shared resources
- Use class names in static methods .class Bytecode as lock object , Because static members belong to classes , Shared by all instance objects , So you need to lock the class
Synchronous code block format :
synchronized( Lock object ){
// The core code for accessing shared resources
}
example :
public class demo {
static int counter = 0;
//static modification , Then the element belongs to the class itself , Does not belong to the object , Load once with class , only one
static final Object room = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (room) {
counter++;
}
}
}, "t1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (room) {
counter--;
}
}
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter);
}
}
Synchronization method
Lock up the core methods that cause thread safety problems , Only one thread can access at a time
synchronized The method of decoration does not have inheritance , So subclasses are thread unsafe , If the methods of subclasses are also synchronized modification , Two lock objects are actually a lock , And it's Subclass objects as locks
usage : Directly add a modifier to the method synchronized
// Synchronization method
Modifier synchronized return type Method name ( Method parameter ) {
Method body ;
}
// Synchronous static method
Modifier static synchronized return type Method name ( Method parameter ) {
Method body ;
}
The bottom layer of the synchronization method also has lock objects :
If the method is an instance method : Default synchronization method this As the lock object of
public synchronized void test() { } // Equivalent to public void test() { synchronized(this) { } }
If the method is static : The synchronization method uses the class name by default .class As the lock object of
class Test{ public synchronized static void test() { } } // Equivalent to class Test{ public void test() { synchronized(Test.class) { } } }
Thread eight locks
Thread eight lock is to inspect synchronized Which object is locked , Direct Baidu search related instances
explain : Mainly focus on whether the locked object is the same
- Lock class objects , The methods of instances of all classes are safe , All instances of the class are equivalent to the same lock
- Lock the this object , It is safe only within the thread of the current instance object , If there are multiple instances, it is not safe
Thread unsafe : Because the same object is not locked , Threads 1 call a Method to lock the class object , Threads 2 call b Method locked n2 object , Not the same object
class Number{
public static synchronized void a(){
Thread.sleep(1000);
System.out.println("1");
}
public synchronized void b() {
System.out.println("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n2.b(); }).start();
}
Thread safety : because n1 call a() Method , Locked are class objects ,n2 call b() Method , Locked objects are also class objects , So thread safety
class Number{
public static synchronized void a(){
Thread.sleep(1000);
System.out.println("1");
}
public static synchronized void b() {
System.out.println("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n2.b(); }).start();
}
Lock principle
Monitor
Monitor Translated as monitor or tube side
Every Java Objects can be associated with a Monitor object ,Monitor It's also class, Its Instances are stored in the heap , If you use synchronized Lock the object ( heavyweight ) after , Of the object's head Mark Word It is set to point to Monitor Object pointer , This is the heavyweight lock
Mark Word structure : The last two are Lock flag position
64 Bit virtual machine Mark Word:
Workflow :
- At the beginning of the Monitor in Owner by null
- When Thread-2 perform synchronized(obj) Will be Monitor Owner Owner Set as Thread-2,Monitor There can only be one of them Owner,obj Object's Mark Word Point to Monitor, hold Object original MarkWord Lock records stored in the thread stack in ( The lightweight lock part is explained in detail )
- stay Thread-2 Locking process ,Thread-3、Thread-4、Thread-5 Also execute synchronized(obj), Will enter EntryList BLOCKED( Double linked list )
- Thread-2 After executing the contents of the synchronized code block , according to obj In the head of the object Monitor Look for the address , Set up Owner It's empty , Set the value of the object header in the lock record of the thread stack back to MarkWord
- Wake up the EntryList The thread waiting in the , The competition is Unfair , If a new thread wants to acquire the lock , May directly seize , Threads that block the queue will continue to block
- WaitSet Medium Thread-0, It's a lock obtained before , But the conditions are not satisfied to enter WAITING Thread in state (wait-notify Mechanism )
Be careful :
- synchronized It has to be in the same object Monitor To achieve the above effect
- No addition synchronized Is not associated with the monitor , Not following the above rules
Bytecode
Code :
public static void main(String[] args) {
Object lock = new Object();
synchronized (lock) {
System.out.println("ok");
}
}
0: new #2 // new Object
3: dup
4: invokespecial #1 // invokespecial <init>:()V, Non virtual method
7: astore_1 // lock quote -> lock
8: aload_1 // lock (synchronized Start )
9: dup // One for initialization , One for quoting
10: astore_2 // lock quote -> slot 2
11: monitorenter // 【 take lock object MarkWord Set as Monitor The pointer 】
12: getstatic #3 // System.out
15: ldc #4 // "ok"
17: invokevirtual #5 // invokevirtual println:(Ljava/lang/String;)V
20: aload_2 // slot 2(lock quote )
21: monitorexit // 【 take lock object MarkWord Reset , Wake up the EntryList】
22: goto 30
25: astore_3 // any -> slot 3
26: aload_2 // slot 2(lock quote )
27: monitorexit // 【 take lock object MarkWord Reset , Wake up the EntryList】
28: aload_3
29: athrow
30: return
Exception table:
from to target type
12 22 25 any
25 28 25 any
LineNumberTable: ...
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 args [Ljava/lang/String;
8 23 1 lock Ljava/lang/Object;
explain :
- By exception try-catch Mechanism , Make sure it will be unlocked
- Method level synchronized Not in bytecode instructions
Lock escalation
Upgrade process
synchronized It's reentrant 、 Unfair heavyweight lock , So it can be optimized
unlocked -> Biased locking -> Lightweight lock -> Heavyweight lock // With the increase of competition , Only lock upgrade , Can't downgrade
Biased locking
The idea of biased lock is biased towards the first thread to acquire the lock object , This thread then reacquires the lock, which no longer requires synchronous operation :
When the lock object is first obtained by the thread, it enters the biased state , Marked as 101, meanwhile Use CAS Operation will thread ID It was recorded that Mark Word. If CAS Successful operation , This thread then enters the lock related synchronization block , Look at this thread ID It means there is no competition , There is no need to synchronize
When there is another thread trying to get the lock object , The bias is over , In this case, withdraw the bias (Revoke Bias) Then return to the unlocked or lightweight lock state

When an object is created :
If you open the bias lock ( Default on ), After the object is created ,MarkWord The value is 0x05 In the end 3 Position as 101,thread、epoch、age All for 0
The bias lock is delayed by default , It doesn't take effect immediately when the program starts , If you want to avoid delays , You can add VM Parameters
-XX:BiasedLockingStartupDelay=0
To disable delay .JDK 8 Delay 4s The reason for opening the deflection lock : At the beginning of code execution , There will be many threads to grab the lock , If the deflection lock is opened, the efficiency will be reducedWhen an object has been evaluated hashCode, You can no longer enter a biased state
add to VM Parameters
-XX:-UseBiasedLocking
Disable bias lock
Undo the state of the deflection lock :
- Call the object's hashCode: Object biased to lock MarkWord Threads are stored in id, call hashCode Cause the deflection lock to be revoked
- When another thread uses a biased lock object , Will upgrade the biased lock to a lightweight lock
- call wait/notify, Application required Monitor, Get into WaitSet
Cancel in bulk : If the object is accessed by multiple threads , But there's no competition , At this point, the thread is biased T1 There's still a chance for us to refocus T2, Heavy bias will reset the object Thread ID
Bulk bias : When the undo bias lock threshold exceeds 20 Next time ,JVM You will feel whether you are biased wrong , Therefore, when locking these objects, we re favor to the locking thread
Cancel in bulk : When the undo bias lock threshold exceeds 40 Next time ,JVM You will feel that you are really biased in the wrong direction , There should be no bias towards , So all objects of the whole class will become unbiased , New objects are also non biased
Lightweight lock
An object has multiple threads to lock , But the locking time is staggered ( There is no competition ), Lightweight locks can be used to optimize , Lightweight locks are transparent to users ( invisible )
Reentrant lock : A thread can enter any block of code in which it already has a lock , The biggest function of reentrant lock is Avoid deadlock
Lightweight locks when there is no competition ( Lock reentry ), Every reentry still needs to be executed CAS operation ,Java 6 Just introduced bias lock to optimize
Lock reentry instance :
static final Object obj = new Object();
public static void method1() {
synchronized( obj ) {
// Synchronized block A
method2();
}
}
public static void method2() {
synchronized( obj ) {
// Synchronized block B
}
}
Create a lock record (Lock Record) object , Per thread Stack frame Will contain a lock record structure , Stores the name of the locked object Mark Word
Let the lock record Object reference Point to the locked object , And try CAS Replace Object Of Mark Word, take Mark Word The value of is stored in the lock record
If CAS Replacement successful , The lock record address and state are stored in the object header 00( Lightweight lock ) , Indicates that the thread locks the object
If CAS Failure , There are two situations :
- If other threads already hold this Object Lightweight lock for , This shows that there is competition , Enter the lock expansion process
- If the thread executes itself synchronized Lock reentry , Just add one Lock Record As a count of reentry
When to exit synchronized Code block ( When the unlock )
- If there is, the value is null Lock record of , It means there is re-entry , The lock record is reset , Indicates the reentry count minus 1
- If the value of the lock record is not null, Use this time CAS take Mark Word Restore the value of to the object header
- success , Then the unlocking is successful
- Failure , It shows that lightweight lock has been expanded or upgraded to heavyweight lock , Enter the heavyweight lock unlock process
Lock expansion
In the process of trying to add a lightweight lock ,CAS The operation cannot succeed , It may be that other threads have added lightweight locks to this object ( There is competition ), At this point, lock expansion is needed , Turn lightweight locks into Heavyweight lock
When Thread-1 When doing lightweight locking ,Thread-0 A lightweight lock has been applied to the object
Thread-1 Failed to add lightweight lock , Enter the lock expansion process : by Object Object application Monitor lock , adopt Object The object header gets the lock thread , take Monitor Of Owner Set as Thread-0, take Object The object header of points to the heavyweight lock address , Then go in on your own Monitor Of EntryList BLOCKED
When Thread-0 When the synchronization block is released , Use CAS take Mark Word Failed to recover the value of to the object header , Then enter the heavyweight unlocking process , Namely, in accordance with the Monitor Address found Monitor object , Set up Owner by null, Wake up the EntryList in BLOCKED Threads
Lock the optimization
spinlocks
When heavyweight locks compete , Threads trying to acquire locks do not immediately block , have access to The spin ( Default 10 Time ) To optimize , Try to acquire the lock in a circular way
Be careful :
- Spin occupation CPU Time , Single core CPU Spin is a waste of time , Because only one thread can run at a time , Multicore CPU Spin is the only way to take advantage
- Threads that fail to spin will enter a blocking state
advantage : It will not enter the blocking state , Reduce the consumption of thread context switching
shortcoming : When more and more threads spin , Will continue to consume CPU resources
Spin lock condition :
Spin success :
Spin failure :
Spin lock description :
- stay Java 6 After that, spin locking is adaptive , For example, the object has just had a successful spin operation , So the possibility of spin success is high , Just spin a few more times ; conversely , Less spin, no spin , Comparative intelligence
- Java 7 After that, you can't control whether to turn on the spin function , from JVM control
// Handwritten spin lock
public class SpinLock {
// Generic is Thread, Atomic reference thread
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void lock() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + " come in");
// Start spinning , The expected value is null, The update value is the current thread
while (!atomicReference.compareAndSet(null, thread)) {
Thread.sleep(1000);
System.out.println(thread.getName() + " Spinning ");
}
System.out.println(thread.getName() + " Spin success ");
}
public void unlock() {
Thread thread = Thread.currentThread();
// After the thread uses the lock, it changes the reference to null
atomicReference.compareAndSet(thread, null);
System.out.println(thread.getName() + " invoke unlock");
}
public static void main(String[] args) throws InterruptedException {
SpinLock lock = new SpinLock();
new Thread(() -> {
// Possessory lock
lock.lock();
Thread.sleep(10000);
// Release the lock
lock.unlock();
},"t1").start();
// Give Way main Thread pause 1 second , bring t1 Threads , Execute first
Thread.sleep(1000);
new Thread(() -> {
lock.lock();
lock.unlock();
},"t2").start();
}
}
Lock elimination
Lock elimination refers to the elimination of locks that are detected to be impossible to compete with shared data , This is a JVM Optimization of instant compiler
Lock elimination is mainly achieved through Escape analysis To support , If the shared data on the heap cannot escape and be accessed by other threads , Then we can treat them as private data , It can also eliminate their locks ( Synchronous elimination :JVM Escape analysis )
Lock coarsening
Lock the same object multiple times , Causes multiple reentry of the thread , Frequent lock operations lead to performance loss , Lock coarsening can be used to optimize
If the virtual machine detects a series of operations that lock the same object , It will extend the range of lock ( Coarsening ) To the outside of the entire sequence of operations
Some code that looks unlocked , In fact, it implicitly adds a lot of locks :
public static String concatString(String s1, String s2, String s3) { return s1 + s2 + s3; }
String Is an immutable class , The compiler will String Automatic optimization of splicing . stay JDK 1.5 Before , Turn into StringBuffer Continuity of objects append() operation , Every append() There is a synchronization block in the method
public static String concatString(String s1, String s2, String s3) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); sb.append(s3); return sb.toString(); }
Expand to the first append() Before operation until the last append() After the operation , Just lock it once
More locks
Lots of irrelevant locks : A big room has two functions to sleep 、 Study , Irrelevant . Now one has to learn , One needs to sleep , If only one room ( An object lock ) Words , Then the concurrency is very low
Subdivide the granularity of locks :
- benefits , It can enhance concurrency
- Disadvantage , If a thread needs to acquire multiple locks at the same time , It's prone to deadlock
resolvent : Prepare multiple object locks
public static void main(String[] args) {
BigRoom bigRoom = new BigRoom();
new Thread(() -> {
bigRoom.study(); }).start();
new Thread(() -> {
bigRoom.sleep(); }).start();
}
class BigRoom {
private final Object studyRoom = new Object();
private final Object sleepRoom = new Object();
public void sleep() throws InterruptedException {
synchronized (sleepRoom) {
System.out.println("sleeping 2 Hours ");
Thread.sleep(2000);
}
}
public void study() throws InterruptedException {
synchronized (studyRoom) {
System.out.println("study 1 Hours ");
Thread.sleep(1000);
}
}
}
Activity
Deadlock
formation
Deadlock : Multiple threads are blocked at the same time , One or all of them are waiting for a resource to be released , Because the thread is blocked indefinitely , Therefore, the program cannot terminate normally
Java Four necessary conditions for deadlock generation :
- mutual exclusion , That is, when a resource is used by a thread ( occupy ) when , Other threads cannot use
- The condition of indivisibility , The resource requester cannot forcibly seize the resource from the resource owner , Resources can only be released by the resource owner
- Request and hold conditions , In other words, when the resource requester requests other resources, it keeps the possession of the original resources
- Loop wait condition , That is, there is a waiting loop queue :p1 want p2 Resources for ,p2 want p1 Resources for , A waiting loop is formed
When all four conditions are true , A deadlock is formed . Break any of the above conditions in case of deadlock , The deadlock will disappear
public class Dead {
public static Object resources1 = new Object();
public static Object resources2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
// Threads 1: Occupancy resources 1 , Request resources 2
synchronized(resources1){
System.out.println(" Threads 1 Already occupied resources 1, Start requesting resources 2");
Thread.sleep(2000);// Two seconds off , Prevent threads 1 Run directly .
//2 Threads in seconds 2 It can definitely lock resources 2
synchronized (resources2){
System.out.println(" Threads 1 Already occupied resources 2");
}
}).start();
new Thread(() -> {
// Threads 2: Occupancy resources 2 , Request resources 1
synchronized(resources2){
System.out.println(" Threads 2 Already occupied resources 2, Start requesting resources 1");
Thread.sleep(2000);
synchronized (resources1){
System.out.println(" Threads 2 Already occupied resources 1");
}
}}
}).start();
}
}
location
Method of locating deadlock :
Use jps Positioning process id, Reuse
jstack id
Positioning deadlocks , Find the deadlock thread and check the source code , Solution optimization"Thread-1" #12 prio=5 os_prio=0 tid=0x000000001eb69000 nid=0xd40 waiting formonitor entry [0x000000001f54f000] java.lang.Thread.State: BLOCKED (on object monitor) # Omit "Thread-1" #12 prio=5 os_prio=0 tid=0x000000001eb69000 nid=0xd40 waiting for monitor entry [0x000000001f54f000] java.lang.Thread.State: BLOCKED (on object monitor) # Omit Found one Java-level deadlock: =================================================== "Thread-1": waiting to lock monitor 0x000000000361d378 (object 0x000000076b5bf1c0, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x000000000361e768 (object 0x000000076b5bf1d0, a java.lang.Object), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at thread.TestDeadLock.lambda$main$1(TestDeadLock.java:28) - waiting to lock <0x000000076b5bf1c0> (a java.lang.Object) - locked <0x000000076b5bf1d0> (a java.lang.Object) at thread.TestDeadLock$$Lambda$2/883049899.run(Unknown Source) at java.lang.Thread.run(Thread.java:745) "Thread-0": at thread.TestDeadLock.lambda$main$0(TestDeadLock.java:15) - waiting to lock <0x000000076b5bf1d0> (a java.lang.Object) - locked <0x000000076b5bf1c0> (a java.lang.Object) at thread.TestDeadLock$$Lambda$1/495053715
Linux You can go through top First of all, I'll go to CPU Take up high Java process , recycling
top -Hp process id
To locate which thread , Last but not least jstack See the output of each thread stackAvoid deadlock : To avoid deadlock, pay attention to the locking sequence
have access to jconsole Tools , stay
jdk\bin
Under the table of contents
Live lock
Live lock : It means that the task or performer is not blocked , Because some conditions are not satisfied , Causes repeated attempts — Failure — Try — The process of failure
Two threads change each other's end conditions , In the end, no one can end :
class TestLiveLock {
static volatile int count = 10;
static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
// Expectations are reduced to 0 Exit loop
while (count > 0) {
Thread.sleep(200);
count--;
System.out.println(" Thread one count:" + count);
}
}, "t1").start();
new Thread(() -> {
// Expectation exceeds 20 Exit loop
while (count < 20) {
Thread.sleep(200);
count++;
System.out.println(" Thread two count:"+ count);
}
}, "t2").start();
}
}
hunger
hunger : Because the priority of a thread is too low , Never get CPU Scheduling execution , It can't end
wait-ify
Basic use
You need to obtain the object lock before calling Lock object .wait()
,notify Wake up a thread randomly ,notifyAll Wake up all threads to compete CPU
Object class API:
public final void notify(): Wake up a single thread waiting for the object monitor .
public final void notifyAll(): Wake up all threads waiting for the object monitor .
public final void wait(): Causes the current thread to wait , Until another thread calls the object's notify() Method or notifyAll() Method .
public final native void wait(long timeout): A time-bound wait , To n End waiting in milliseconds , Or be awakened
explain :wait Is a suspended thread , What needs to wake up is the pending operation , Blocking threads can compete for locks by themselves , The suspended thread needs to wake up and compete for the lock
contrast sleep():
- The principle is different :sleep() The method belongs to Thread class , Threads are used to control their own processes , Pause the execution of this thread for a period of time and give the execution opportunity to other threads ;wait() Method belongs to Object class , For inter thread communication
- Yes Lock handling mechanism Different : call sleep() In the process of method , The thread does not release the object lock , When calling wait() Method time , The thread will give up the object lock , Enters the wait lock pool waiting for this object ( How can other threads preempt the lock and perform wake-up operations without releasing the lock ), But it will release CPU
- Different use areas :wait() The method must be placed in ** Synchronization control method and synchronization code block ( Get the lock first )** Use in ,sleep() Methods can be used anywhere
Underlying principle :
- Owner Thread discovery condition not met , call wait Method , You can enter WaitSet Turn into WAITING state
- BLOCKED and WAITING All threads are blocked , Not occupy CPU Time slice
- BLOCKED Thread will be Owner Wake up when thread releases lock
- WAITING Thread will be Owner Thread calls notify or notifyAll Wake up when , Waking up doesn't mean that the person gets the lock immediately , Need to go to EntryList Re compete
Code optimization
spurious wakeup :notify Only one can wake up randomly WaitSet Thread in , At this time, if there are other threads waiting , Then the correct thread may not wake up
resolvent : use notifyAll
notifyAll Only solve the wake-up problem of a thread , Use if + wait There is only one chance to judge , Once the conditions are not established , Unable to re judge
resolvent : use while + wait, When conditions don't hold , Again wait
@Slf4j(topic = "c.demo")
public class demo {
static final Object room = new Object();
static boolean hasCigarette = false; // Is there any smoke
static boolean hasTakeout = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (room) {
log.debug(" Do you have any cigarettes ?[{}]", hasCigarette);
while (!hasCigarette) {
//while Prevent false awakening
log.debug(" No smoke , Take a break !");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug(" Do you have any cigarettes ?[{}]", hasCigarette);
if (hasCigarette) {
log.debug(" You can start working ");
} else {
log.debug(" Didn't do it ...");
}
}
}, " Xiaonan ").start();
new Thread(() -> {
synchronized (room) {
Thread thread = Thread.currentThread();
log.debug(" Did the takeout arrive ?[{}]", hasTakeout);
if (!hasTakeout) {
log.debug(" No takeout , Take a break !");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug(" Did the takeout arrive ?[{}]", hasTakeout);
if (hasTakeout) {
log.debug(" You can start working ");
} else {
log.debug(" Didn't do it ...");
}
}
}, " Little girl ").start();
Thread.sleep(1000);
new Thread(() -> {
// Can you add synchronized (room)?
synchronized (room) {
hasTakeout = true;
//log.debug(" Here comes the smoke !");
log.debug(" Here's the takeout !");
room.notifyAll();
}
}, " Take away delivery ").start();
}
}
park-un
LockSupport It is used to create locks and other synchronization classes Thread primitives
LockSupport Class method :
LockSupport.park()
: Pause current thread , Suspend primitiveLockSupport.unpark( Suspended thread object )
: Resume the running of a thread
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("start..."); //1
Thread.sleep(1000);// Thread.sleep(3000)
// First park Again unpark And first unpark Again park The effect is the same , Will directly resume the operation of the thread
System.out.println("park..."); //2
LockSupport.park();
System.out.println("resume...");//4
},"t1");
t1.start();
Thread.sleep(2000);
System.out.println("unpark..."); //3
LockSupport.unpark(t1);
}
LockSupport It appears to enhance wait & notify The function of :
- wait,notify and notifyAll Must cooperate Object Monitor Use it together , and park、unpark Unwanted
- park & unpark In threads To block and wake up threads , and notify Only one waiting thread can be wakened randomly ,notifyAll Is to wake up all waiting threads
- park & unpark You can start with unpark, and wait & notify Not first notify. Analogy production and consumption , Consume first and consume when you find products , No, just wait ; As soon as you produce, you directly produce goods , Then the thread consumes directly
- wait Lock resources will be released into the waiting queue ,park Lock resources will not be released , Only responsible for blocking the current thread , Will release CPU
principle : Similar producers and consumers
- First park:
- Current thread call Unsafe.park() Method
- Check _counter , This situation is 0, Then get _mutex The mutex
- Thread entry _cond Conditional variables hang
- call Unsafe.unpark(Thread_0) Method , Set up _counter by 1
- Wake up the _cond In a conditional variable Thread_0,Thread_0 Resume operation , Set up _counter by 0
First unpark:
- call Unsafe.unpark(Thread_0) Method , Set up _counter by 1
- Current thread call Unsafe.park() Method
- Check _counter , This situation is 1, At this time, the thread does not need to hang , Continue operation , Set up _counter by 0
Safety analysis
Member and static variables :
- If they don't share , Then thread safety
- If they're shared , Depending on whether their state can be changed , There are two situations :
- If there are only read operations , Then thread safety
- If there are read and write operations , Then this code is a critical area , Thread safety needs to be considered
local variable :
- Local variables are thread safe
- Objects referenced by local variables are not necessarily thread safe ( Escape analysis ):
- If the object does not escape the function of the method, access , It's thread safe ( Each method has a stack frame )
- If the object escapes the scope of the method , Thread safety needs to be considered ( Expose references )
Common thread safety classes :String、Integer、StringBuffer、Random、Vector、Hashtable、java.util.concurrent package
Thread safe means , When multiple threads call a method of their same instance , It's thread safe
Each method is atomic , But the combination of multiple methods is not atomic , Only the internal security of the called method can be guaranteed :
Hashtable table = new Hashtable(); // Threads 1, Threads 2 if(table.get("key") == null) { table.put("key", value); }
Stateless thread safety , Is a class without member variables
Immutable classes are thread safe :String、Integer And so on are immutable classes , The internal state cannot be changed , So the method is thread safe
replace The bottom layer of such methods is to create an object , Copy the past
Map<String,Object> map = new HashMap<>(); // Thread unsafe String S1 = "..."; // Thread safety final String S2 = "..."; // Thread safety Date D1 = new Date(); // Thread unsafe final Date D2 = new Date(); // Thread unsafe ,final Give Way D2 The referenced object cannot be changed , But the content of the object can change
If the abstract method has parameters , Uncertain behavior after being rewritten may cause thread insecurity , It's called the alien method :public abstract foo(Student s);
Synchronous mode
Protective suspension
Single task version
Guarded Suspension, It is used when one thread waits for the execution result of another thread
- There is a result that needs to be passed from one thread to another , Associate them with the same GuardedObject
- If there are results continuously from one thread to another, you can use message queuing ( See the producer / consumer )
- JDK in ,join The implementation of the 、Future The implementation of the , That's the model
public static void main(String[] args) {
GuardedObject object = new GuardedObjectV2();
new Thread(() -> {
sleep(1);
object.complete(Arrays.asList("a", "b", "c"));
}).start();
Object response = object.get(2500);
if (response != null) {
log.debug("get response: [{}] lines", ((List<String>) response).size());
} else {
log.debug("can't get response");
}
}
class GuardedObject {
private Object response;
private final Object lock = new Object();
// To get the results
//timeout : Maximum waiting time
public Object get(long millis) {
synchronized (lock) {
// 1) Record the initial time
long begin = System.currentTimeMillis();
// 2) Time that has passed
long timePassed = 0;
while (response == null) {
// 4) hypothesis millis yes 1000, It turns out that 400 When I woke up , Then there are 600 Have to wait
long waitTime = millis - timePassed;
log.debug("waitTime: {}", waitTime);
// Exit the cycle after the maximum waiting time is exceeded
if (waitTime <= 0) {
log.debug("break...");
break;
}
try {
lock.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 3) If you wake up early , The elapsed time is assumed to be 400
timePassed = System.currentTimeMillis() - begin;
log.debug("timePassed: {}, object is null {}",
timePassed, response == null);
}
return response;
}
}
// Produce results
public void complete(Object response) {
synchronized (lock) {
// Conditions met , Notification waiting thread
this.response = response;
log.debug("notify...");
lock.notifyAll();
}
}
}
Multitask version
Multi tasking protection pause :
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new People().start();
}
Thread.sleep(1000);
for (Integer id : Mailboxes.getIds()) {
new Postman(id, id + " The express arrived on the th ").start();
}
}
@Slf4j(topic = "c.People")
class People extends Thread{
@Override
public void run() {
// Receive letters
GuardedObject guardedObject = Mailboxes.createGuardedObject();
log.debug(" Start receiving letters i d:{}", guardedObject.getId());
Object mail = guardedObject.get(5000);
log.debug(" Received a letter id:{}, Content :{}", guardedObject.getId(),mail);
}
}
class Postman extends Thread{
private int id;
private String mail;
// Construction method
@Override
public void run() {
GuardedObject guardedObject = Mailboxes.getGuardedObject(id);
log.debug(" Start sending letters i d:{}, Content :{}", guardedObject.getId(),mail);
guardedObject.complete(mail);
}
}
class Mailboxes {
private static Map<Integer, GuardedObject> boxes = new Hashtable<>();
private static int id = 1;
// Produce a unique id
private static synchronized int generateId() {
return id++;
}
public static GuardedObject getGuardedObject(int id) {
return boxes.remove(id);
}
public static GuardedObject createGuardedObject() {
GuardedObject go = new GuardedObject(generateId());
boxes.put(go.getId(), go);
return go;
}
public static Set<Integer> getIds() {
return boxes.keySet();
}
}
class GuardedObject {
// identification ,Guarded Object
private int id;// add to get set Method
}
Sequential output
Sequential output 2 1
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (true) {
//try { Thread.sleep(1000); } catch (InterruptedException e) { }
// When there is no permission , The current thread is suspended ; With permission , Use this License , The current thread resumes running
LockSupport.park();
System.out.println("1");
}
});
Thread t2 = new Thread(() -> {
while (true) {
System.out.println("2");
// To thread t1 issue 『 The license 』( Multiple consecutive calls unpark Only one... Will be issued 『 The license 』)
LockSupport.unpark(t1);
try {
Thread.sleep(500); } catch (InterruptedException e) {
}
}
});
t1.start();
t2.start();
}
Alternate output
Continuous output 5 Time abc
public class day2_14 {
public static void main(String[] args) throws InterruptedException {
AwaitSignal awaitSignal = new AwaitSignal(5);
Condition a = awaitSignal.newCondition();
Condition b = awaitSignal.newCondition();
Condition c = awaitSignal.newCondition();
new Thread(() -> {
awaitSignal.print("a", a, b);
}).start();
new Thread(() -> {
awaitSignal.print("b", b, c);
}).start();
new Thread(() -> {
awaitSignal.print("c", c, a);
}).start();
Thread.sleep(1000);
awaitSignal.lock();
try {
a.signal();
} finally {
awaitSignal.unlock();
}
}
}
class AwaitSignal extends ReentrantLock {
private int loopNumber;
public AwaitSignal(int loopNumber) {
this.loopNumber = loopNumber;
}
// Parameters 1: Print the content Parameter two : Condition variables, Parameter two : Wake up the next
public void print(String str, Condition condition, Condition next) {
for (int i = 0; i < loopNumber; i++) {
lock();
try {
condition.await();
System.out.print(str);
next.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
unlock();
}
}
}
}
Asynchronous mode
The traditional version
The producer of asynchronous mode / consumer :
class ShareData {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws Exception{
// Synchronization code block , Lock
lock.lock();
try {
// Judge Prevent false awakening
while(number != 0) {
// Waiting can't produce
condition.await();
}
// work
number++;
System.out.println(Thread.currentThread().getName() + "\t " + number);
// notice Wake up the
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws Exception{
// Synchronization code block , Lock
lock.lock();
try {
// Judge Prevent false awakening
while(number == 0) {
// Waiting can't be spent
condition.await();
}
// work
number--;
System.out.println(Thread.currentThread().getName() + "\t " + number);
// notice Wake up the
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class TraditionalProducerConsumer {
public static void main(String[] args) {
ShareData shareData = new ShareData();
// t1 Threads , production
new Thread(() -> {
for (int i = 0; i < 5; i++) {
shareData.increment();
}
}, "t1").start();
// t2 Threads , consumption
new Thread(() -> {
for (int i = 0; i < 5; i++) {
shareData.decrement();
}
}, "t2").start();
}
}
Improved version
The producer of asynchronous mode / consumer :
- Consumption queues can be used to balance thread resources for production and consumption , There is no need for one-to-one correspondence between threads that produce and consume results
- The producer is only responsible for generating the resulting data , I don't care what to do with the data , And consumers are focused on the result data
- Message queues are capacity limited , When full, no more data will be added , Empty time will not consume data
- JDK Various blocking queues in , That's the model
public class demo {
public static void main(String[] args) {
MessageQueue queue = new MessageQueue(2);
for (int i = 0; i < 3; i++) {
int id = i;
new Thread(() -> {
queue.put(new Message(id," value "+id));
}, " producer " + i).start();
}
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
Message message = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}," consumer ").start();
}
}
// Message queue class ,Java Communication between threads
class MessageQueue {
private LinkedList<Message> list = new LinkedList<>();// Queue collection of messages
private int capacity;// Queue capacity
public MessageQueue(int capacity) {
this.capacity = capacity;
}
// Get message
public Message take() {
// Check if the queue is empty
synchronized (list) {
while (list.isEmpty()) {
try {
sout(Thread.currentThread().getName() + ": The queue is empty , Consumer thread waiting ");
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Get the message from the head of the queue and return
Message message = list.removeFirst();
sout(Thread.currentThread().getName() + ": Consumed news --" + message);
list.notifyAll();
return message;
}
}
// Save the message
public void put(Message message) {
synchronized (list) {
// Check that the queue is full
while (list.size() == capacity) {
try {
sout(Thread.currentThread().getName()+": The queue is full , Producer thread waiting ");
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Add the message to the end of the queue
list.addLast(message);
sout(Thread.currentThread().getName() + ": The message has been produced --" + message);
list.notifyAll();
}
}
}
final class Message {
private int id;
private Object value;
//get set
}
Blocking queues
public static void main(String[] args) {
ExecutorService consumer = Executors.newFixedThreadPool(1);
ExecutorService producer = Executors.newFixedThreadPool(1);
BlockingQueue<Integer> queue = new SynchronousQueue<>();
producer.submit(() -> {
try {
System.out.println(" production ...");
Thread.sleep(1000);
queue.put(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
consumer.submit(() -> {
try {
System.out.println(" Waiting for consumption ...");
Integer result = queue.take();
System.out.println(" The result is :" + result);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
边栏推荐
猜你喜欢
【带你上手云原生体系】第四部分:Kubernetes从入门到精通
What should we pay attention to in writing automated test resumes? What are the skills?
【ManageEngine】加强企业特权访问安全性的7个方法
Cyberspace mapping
Best practices for three node hybrid deployment
torch. jit. Trace and torch jit. Differences between scripts
Idea décompile toute la source du paquet jar
一个合约能存储多少数据?
Talk about the top 10 mistakes often made in implementing data governance
The undeclared identifier "MainWindow" is used in the QT code of Kirin system“
随机推荐
Leetcode-720: the longest word in a dictionary
leetcode-2337:移动片段得到字符串
Daily question C language 9
LeetCode_ Dynamic programming_ Difficulties_ 44. Wildcard matching
2022.7.21-----leetcode. eight hundred and fourteen
Best practices for monitoring tidb with grafana
leetcode-676:实现一个魔法字典
另类加法与走方格的方案数
PD scheduling strategy best practices
面试项目准备 | 如何向面试官展示项目?
Auto. JS learning note 18: sub thread and timer are used together (an example is at the end)
小知识点随笔记
三节点混合部署的最佳实践
Apachecon Asia 2022 opens registration: pulsar technology issues make a big debut
基于 ABP 实现 DDD-- 领域服务、应用服务和 DTO 实践
Leetcode-6113: the smallest number in an infinite set
Tidb high concurrency write scenario best practices
torch.jit.trace与torch.jit.script的区别
Leetcode-6116: calculate the value of Boolean binary tree
支持百万并发的服务器测试