After reading the course “Java Concurrent Programming” in Geek Time last week, I found that the knowledge is easy to understand without many obscure words, which I think is suitable for readers who are beginning to Java concurrent programming. For technical bull, don’t spend this 100 yuan to buy the course, or suggest to read the classic “holy book”. This article will not write too much about the specific content of the course, the author hopes that interested readers to spend money to buy the course, creation is not easy, “spending money to buy the knowledge of others” is a virtuous circle.
The following section records only the programming paradigms mentioned in the course that are recommended for concurrent programming in practical projects. Normal means classic, so there’s no particular reason not to try to write it differently.
Optimize circular waiting with a wait-notification mechanism
When the thread requirements are met, the waiting thread is notified using the notify() and notifyAll() methods of Java objects. This process is outlined in the figure below. Calling notify() when a condition is met notifies the thread in the wait queue (a mutex wait queue) that the condition has been met.
Note: becauseNotify () can only guarantee that the condition is satisfied at the notification point in time. While being notified of the threadExecution point in time and notification point in timeThere is almost no overlap, so when the thread executes, it is likely that the condition will not be satisfied (because another thread may cut the queue).
while{wait(); }Copy the code
Using the above paradigm can solve the problem that the conditions mentioned above have ever been met. Because by the time wait() returns, it is possible that the condition has changed, once the condition was satisfied, but now it is not, and the condition is rechecked to see if it is satisfied.
// This sample code will be easier to understand if you refer to the background in the course
class Allocator {
private List<Object> als;
// Request all resources at once
synchronized void apply( Object from, Object to){
//
while(als.contains(from) ||
als.contains(to)){
try{
wait();
}catch(Exception e){
}
}
als.add(from);
als.add(to);
}
// Return the resource
synchronized void free( Object from, Object to){ als.remove(from); als.remove(to); notifyAll(); }}Copy the code
Lock acquisition and release
lock.lock();
try{}finally {
// Ensure that the lock can be released
lock.unlock();
}
Copy the code
A classic example of the use of locks in the Java SDK (above) is try{}finally{}. The key is to release the Lock in finally.
// This sample code will be easier to understand if you refer to the background in the course
class X {
private final Lock rtl =
new ReentrantLock();
int value;
public void addOne(a) {
/ / acquiring a lock
rtl.lock();
try {
value+=1;
} finally {
// Ensure that the lock can be releasedrtl.unlock(); }}}Copy the code
Lockless utility class
// This abstract snippet of code is common in many lock-free programs
do {
// Get the current valueOldV = XXXX;// Calculate the new value based on the current valuenewV = ... oldV... }while(! compareAndSet(oldV,newV);Copy the code
In Java version 1.8, AtomicLong’s getAndIncrement() method is modded to unsafe.getAndAddLong(). As you can see from the JDK source code below, the o and offset arguments uniquely determine the memory address of the shared variable.
public final long getAndAddLong(
Object o, long offset, long delta){
long v;
do {
// Read the value in memory
v = getLongVolatile(o, offset);
} while(! compareAndSwapLong( o, offset, v, v + delta));return v;
}
// Atomically update the variable to x
// The condition is that the value in memory equals the expected
// Return true if the update succeeds
native boolean compareAndSwapLong(
Object o, long offset,
long expected,
long x);
Copy the code
ThreadLocal and memory leaks
Why might using ThreadLocal in a thread pool cause a memory leak? The reason for this is that threads live too long in Thread pools and are often co-existing with applications, which means that threadLocalMaps held by threads are never collected. In addition, an Entry in a ThreadLocalMap is a WeakReference to a ThreadLocal, so it can be reclaimed once the ThreadLocal ends its life cycle. However, the Value in an Entry is strongly referenced by the Entry. Therefore, the Value cannot be reclaimed even after its life cycle ends, resulting in memory leakage. Manual release is required.
ExecutorService es;
ThreadLocal tl;
es.execute(()->{
//ThreadLocal adds variables
tl.set(obj);
try {
// Omit the business logic code
}finally {
// Manually clean ThreadLocaltl.remove(); }});Copy the code
Termination of the thread
In two-phase termination mode, the first thing we need to do is to convert the thread rptThread state to RUNNABLE, which is as simple as calling rptThread.interrupt(). How to gracefully terminate an rptThread after its state transitions to RUNNABLE? In the following example code, the flag bit we choose is the interrupt status of the thread: Thread.currentthread ().isinterrupted () Reset Thread interrupt status with Thread.currentThread().interrupt() because exception handling by the JVM clears Thread interrupt status.
In practical work, the following code should be used as far as possible. The reason is that it is possible to call a third party library in a Thread’s run() method, and there is no way to ensure that the third party library handles the interrupt exception correctly. For example, after catching the interrupt exception thrown by Thread.sleep(), the third party library does not reset the interrupt state of the Thread. This will cause the thread to fail to terminate properly. It is highly recommended that you set your own thread termination flags, such as isTerminated in the code below. This does not prevent the thread from gracefully terminating, regardless of whether the interrupt exception is handled correctly.
class Proxy {
// Thread termination flag bit
volatile boolean terminated = false;
boolean started = false;
// Collect threads
Thread rptThread;
Enable the collection function
synchronized void start(a){
// It is not allowed to start multiple collection threads simultaneously
if (started) {
return;
}
started = true;
terminated = false;
rptThread = new Thread(()->{
while(! terminated){// omit the collection, return implementation
report();
// Data is collected and returned every two seconds
try {
Thread.sleep(2000);
} catch (InterruptedException e){
// Reset the thread interrupt stateThread.currentThread().interrupt(); }}// The thread terminates immediately
started = false;
});
rptThread.start();
}
// Stop the collection function
synchronized void stop(a){
// Set the interrupt flag bit
terminated = true;
// Interrupt thread rptThreadrptThread.interrupt(); }}Copy the code