In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article will explain in detail the use of blocking and awakening API provided by Object. The editor thinks it is very practical, so I share it with you as a reference. I hope you can get something after reading this article.
Blocking and Wake up API provided by Object
Java.lang.Object is the base class for all non-basic types, that is, all subclasses of java.lang.Object have blocking and wake-up capabilities. The blocking and wake-up API provided by Object are analyzed in detail below. They have something in common: they must be used in code blocks or methods modified by synchronized.
Blocking wait-wait
The wait-wait () method provides blocking functionality, which is divided into timeout and permanent blocking versions. In fact, the underlying layer provides only one JNI method:
/ / this is the JNI method provided by the underlying layer, with timeout blocking wait, response interrupt, the other two are just variants
Public final native void wait (long timeoutMillis) throws InterruptedException
/ / variant method 1, permanent blocking, response interrupt
Public final void wait () throws InterruptedException {
Wait (0L)
}
/ / variant method 2, with timeout blocking, the timeout is divided into two periods: millisecond and nanosecond, actually nanosecond greater than 0 directly millisecond plus 1 (so violent.), response interrupt
Public final void wait (long timeoutMillis, int nanos) throws InterruptedException {
If (timeoutMillis
< 0) { throw new IllegalArgumentException("timeoutMillis value is negative"); } if (nanos < 0 || nanos >999999) {
Throw new IllegalArgumentException ("nanosecond timeout value out of range")
}
If (nanos > 0) {
TimeoutMillis++
}
Wait (timeoutMillis)
}
That is, only one wait (long timeoutMillis) method is the JNI interface, and the other two methods are equivalent to:
Wait () is equivalent to wait (0L). Wait (long timeoutMillis, int nanos) is equivalent to wait (timeoutMillis + 1L) when parameters are valid.
Because wait (long timeoutMillis, int nanos) is the method with the most complete parameters, and its API comments are very long, here we directly translate and extract the core elements of its comments:
The current thread blocks and waits until it is woken up. There are generally three cases of waking up: notify (All) is called, the thread is interrupted, or the specified blocking time is exceeded if the timeout blocking is specified. The current thread must acquire the monitor lock ("monitor lock") for this object, that is, "before a thread can call the blocking wait method, a thread must become the owner of the monitor lock for this object." After using the wait () method, the current thread places itself in the wait collection (wait-set) of the current object, and then releases all then to relinquish any nd all synchronization claims on this object on this object, keeping in mind that only the synchronization declaration on the current object will be released, and the current thread's synchronization lock on other objects will not be released until its wait () method is called. "Warning": after the thread is woken up (notify () or interrupt), it is removed from the waiting set (wait-set) and re-allowed to be scheduled by the thread scheduler. Normally, this awakened thread competes with other threads for synchronization rights (locks) on the object, and once the thread regains "control of the object" (regained control of the object), all synchronization declarations on the object are restored to the previous state, that is, when the wait () method is called (in my opinion, before the wait () method is called). If any thread is in a blocking wait state before it calls wait (), or after calling the wait () method, once the thread calls Thread#interrupt (), the thread interrupts and throws an InterruptedException exception, and the thread's interrupted state is cleared. The InterruptedException exception is delayed until point 4 mentions that "all its synchronization declarations on the object are restored to the previous state."
It is also worth noting:
"A thread must be the owner of the monitor lock for this object to call the wait () series of methods normally, that is, the wait () series of methods must be called in the synchronous code block (synchronized code block), otherwise an IllegalMonitorStateException exception will be thrown." this is a common problem for beginners or developers who do not understand the mechanism of wait ().
The above five-point description can be summarized by writing a simple synchronization code block pseudo-code timing:
Final Object lock = new Object ()
Synchronized (lock) {
1. The thread enters the synchronization code block, which means that the object monitor lock was acquired successfully.
While (! condition) {
Lock.wait (); 2. The thread calls wait () to block and wait
Break
}
3. The thread is awakened from the blocking wait of wait () and returns to the synchronized state after step 1
4. Continue to execute the rest of the code until you leave the synchronization block
}
Wake up-notify
The method signature of the notify () method is as follows:
@ HotSpotIntrinsicCandidate
Public final native void notify ()
The following is the usual translation of its API comments:
Waking up a thread that blocks waiting on this object monitor (if there are multiple blocking threads) is arbitrary as to which thread to choose to wake up, depending on the reality, a thread can block on the object monitor by calling the wait () method. The awakened thread does not immediately continue execution until the current thread (that is, the thread that currently calls the notify () method) releases the lock on the object. The awakened thread competes with other threads to synchronize on the object (in other words, it can continue execution only if it has synchronization control over the object), and the awakened thread has no reliable privileges or disadvantages when it becomes the next thread to lock the object. This method can only be called when a thread takes ownership (the owner) of this object monitor, specifically in a synchronization method, in a synchronization code block, or in a static synchronization method. Otherwise, an IllegalMonitorStateException exception is thrown. Wake up all-notifyAll
The method signature of the notifyAll () method is as follows:
@ HotSpotIntrinsicCandidate
Public final native void notifyAll ()
Wake up all threads that block waiting on this object monitor, and a thread can block on the object monitor by calling the wait () method.
The description of other annotations is similar to the notify () method.
Summary of synchronized
The use of the synchronized keyword is mentioned in the materials we often see:
The normal synchronization method synchronizes or locks the current instance object. The static synchronization method synchronizes or locks the Class object of the current instance object. Synchronize the code block, synchronize or lock the instance object in parentheses.
For synchronous code blocks, the synchronized keyword abstraction to the bytecode level means that the bytecode in the synchronous code block is executed between the monitorenter and monitorexit instructions:
Synchronized (xxxx) {
... coding block
}
↓
Monitorenter
.. coding block-bytecode
Monitorexit
JVM needs to ensure that each monitorenter has a monitorexit corresponding to it. Any object has a monitor (actually an ObjectMonitor) associated with it, and if and only if a monitor is held, it will be locked. When the thread executes the monitorenter instruction, it attempts to acquire the monitor ownership corresponding to the object, that is, it attempts to acquire the lock of the object.
For synchronous (static) methods, the synchronized method is translated into ordinary method calls and return instructions, such as invokevirtual, etc. There are no special instructions at the JVM bytecode level to implement the method modified by synchronized, but the synchronized flag position 1 in the access_flags field of the method in the method table of the Class file Indicates that the method is a synchronous method and uses the object calling the method or the Class to which the method belongs to represent the Klass as a lock object inside the JVM.
In fact, from the developer's point of view, "these two ways are just different in the timing of acquiring locks."
The following is a repetition of "several problems that seem unreasonable at first glance but are true" (in fact, they have been mentioned earlier):
When a thread enters a synchronized method or code block, it is equivalent to successfully acquiring the monitor lock. If the wait () series method is successfully called at this time, it immediately releases the monitor lock and adds it to the waiting set (Wait Set) for blocking waiting. Since a thread has released the monitor lock, it can call the notify (All) method to wake up the blocking thread in the waiting set after another thread enters the synchronized method or code block, but this wake-up operation does not take effect immediately after the notify (All) method is called, but only after the thread "exits the synchronized method or code block". The thread awakened from the blocking of the wait () method competes for control of the monitor target object. Once the object is regained control, the synchronization state of the thread is restored to the state it was when it stepped into the synchronized method or code block (that is, the state when the object monitor lock was successfully acquired) before the thread can continue to execute.
To verify these three points, write a simple Demo:
Public class Lock {
}
Public class WaitMain {
Private static final DateTimeFormatter F = DateTimeFormatter.ofPattern ("yyyy-MM-dd HH:mm:ss.SSS")
Public static void main (String [] args) throws Exception {
/ / there is no problem changing to Object here.
Final Lock lock = new Lock ()
New Thread (new WaitRunnable (lock), WaitThread-1) .start ()
New Thread (new WaitRunnable (lock), WaitThread-2) .start ()
Thread.sleep (50)
New Thread (new NotifyRunnable (lock), NotifyThread) .start ()
Thread.sleep (Integer.MAX_VALUE)
}
@ RequiredArgsConstructor
Private static class WaitRunnable implements Runnable {
Private final Lock lock
@ Override
Public void run () {
Synchronized (lock) {
System.out.println (String.format ("[% s]-thread [% s] acquired lock successfully, ready to execute wait method", F.format (LocalDateTime.now ()
Thread.currentThread () .getName ())
While (true) {
Try {
Lock.wait ()
} catch (InterruptedException e) {
/ / ignore
}
System.out.println (String.format ("[% s]-Thread [% s] wakes up from wait, prepare exit", F.format (LocalDateTime.now ()
Thread.currentThread () .getName ())
Try {
Thread.sleep (500)
} catch (InterruptedException e) {
/ / ignore
}
Break
}
}
}
}
@ RequiredArgsConstructor
Private static class NotifyRunnable implements Runnable {
Private final Lock lock
@ Override
Public void run () {
Synchronized (lock) {
System.out.println (String.format ("[% s]-thread [% s] acquired lock successfully, ready to execute notifyAll method", F.format (LocalDateTime.now ()
Thread.currentThread () .getName ())
Lock.notifyAll ()
System.out.println (String.format ("[% s]-thread [% s] hibernates 3000ms first", F.format (LocalDateTime.now ())
Thread.currentThread () .getName ())
Try {
Thread.sleep (3000)
} catch (InterruptedException e) {
/ / ignore
}
System.out.println (String.format ("[% s]-Thread [% s] prepare exit", F.format (LocalDateTime.now ()
Thread.currentThread () .getName ())
}
}
}
}
The execution result at some point is as follows:
[2019-04-27 23 28 17.617]-Thread [WaitThread-1] acquired the lock successfully and is ready to execute the wait method
[2019-04-27 23 28 17.631]-Thread [WaitThread-2] acquired the lock successfully, ready to execute the wait method
[2019-04-27 23 28 17.657]-Thread [NotifyThread] acquired the lock successfully, ready to execute the notifyAll method {
Try {
System.out.println (String.format ("[% s]-Task 2 starts execution for 4 seconds...", LocalDateTime.now () .format (F)
Thread.sleep (4000)
System.out.println (String.format ("[% s]-Task II execution ends...", LocalDateTime.now () .format (F)
} catch (Exception e) {
/ / ignore
}
});
ThreadPool.execute (()-> {
Try {
System.out.println (String.format ("[% s]-Task 3 starts execution for 5 seconds...", LocalDateTime.now () .format (F)
Thread.sleep (5000)
System.out.println (String.format ("[% s]-Task 3 execution ends...", LocalDateTime.now () .format (F)
} catch (Exception e) {
/ / ignore
}
});
Thread.sleep (Integer.MAX_VALUE)
}
}
The result of one execution is as follows:
[2019-04-29 04-29 02VOG 07VOV 25.465]-Task II begins execution for 4 seconds.
[2019-04-29 04-29 02VOG 07VOV 25.465]-the task begins and lasts for 3 seconds.
[2019-04-29 02VO7VOV 28.486]-as soon as the mission is over.
[2019-04-29 02VO7 VLV 28.486]-Task 3 begins execution for 5 seconds.
[2019-04-29 02VO7 VLV 29.486]-Task II execution is over.
[2019-04-29 02VO7 VOL33.487]-Task 3 execution is over.
Summary
In view of the fact that the author is not good at C linguistics, there is no in-depth analysis of the implementation of the JVM source code. We can only combine some existing materials and my own understanding to re-sort out the blocking and wake-up mechanisms provided by Object. Combined with the source code of JUC synchronizer, I realized that JUC synchronizer only used Java language to implement the blocking and awakening mechanism of C language in JVM, that is, the JNI methods provided by Object at the data structure and algorithm level.
Finally, the blocking wait and wake-up mechanism provided by Object is implemented by JVM (if you are particularly familiar with the C language, you can study its implementation through the JVM source code, for most developers, this part of the knowledge is actually a black box), unless it is particularly proficient or the JDK version is too low to introduce the JUC package (the JUC package is JDK1.5 or later added to the JDK). In general, "Object should not be preferred". On the one hand, the API provided by Object is a Native method, and its function may be affected by the JVM version (which may bring positive or negative effects such as performance improvement). On the other hand, the API provided by Object is not flexible. To sum up, it is more recommended to use lock-related class libraries in JUC packages specially designed for concurrency, such as reentrant locks ReentrantLock.
❝
Having said that, the author found that in some versions after JDK1.5, some of the class libraries that originally used ReentrantLock have been replaced with API and synchronized provided by Object because they prefer to use synchronized and Object Monitor. I can't remember which classes I saw a long time ago when I looked at the comments on the source code. It'seems that JDK developers also have a temper.
❞
Until JDK11, there are a large number of JDK class libraries that use the API provided by Object and the blocking and wake-up capabilities implemented by the synchronized keyword, which makes sense.
This is the end of the article on "what is the use of blocking and awakening API provided by Object". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.