This article is part of my notes from reading the Art of Concurrent Programming in Java, which I have compiled into a GitBook ebook (a work in progress) that might be a better reading experience. Long articles like this are hard to read, bookable, but never read. If necessary, you can follow the wechat public number Nobita and learn programming together with you and reply in the background I love Java to receive (PS: do not want to follow and want to see this note, you can see the link at the end of the article.)
Content abstract
This article is relatively long, mainly introduces the basic concept and significance of thread, multithreaded program development needs to pay attention to the problem, the way to create thread, thread synchronization, thread communication, thread life cycle, atomic class and so on.
These contents are basically from the book “Java Concurrent Programming art”, in this thank you, I read on wechat for free, so it is a white piao. Part of the source code interpretation is the author himself from the JDK source strip down.
Definition and meaning of threads
Definition of thread
- Is a lightweight process, threads are cheaper to create and switch than processes
- Multiple threads in the same process share all system resources in that process, such as virtual address space, file descriptors, signal processing, and so on
- Is the smallest unit in which an operating system can schedule operations
- Java programs have at least one thread, main, which is created by the JVM
Why multithreading
- Can take full advantage of multiprocessor cores
- Faster response times and the ability to delegate tasks that require less data consistency to other threads
- Better programming models, such as the producer-consumer model, can be decoupled
Concurrent programming issues to be aware of
Context switch
The CPU executes tasks through time slices, and multiple threads scramble for time slices on the CPU for execution. Thread switching needs to save some states, and switching back again needs to restore the states, which is the context switching cost.
So it’s not that more threads are faster, and frequent switching costs performance
Ways to reduce context switching:
- Lockless concurrent programming: for example, divide a pile of data into several pieces and hand them over to different threads to avoid using locks
- Using CAS: Using spin without locking can reduce thread contention switching, but can be more CPU intensive
- Use the least number of threads
- Using coroutines: Perform multiple tasks in one thread
A deadlock
Deadlock is the phenomenon of threads waiting for each other due to the improper processing of resources
Ways to avoid deadlocks:
- Avoid one thread acquiring multiple locks at the same time
- Avoid a thread occupying multiple resources in a lock at the same time. Try to ensure that each lock occupies only one resource
- Try using timing lock, lock.tryLock(timeout)
- For database locks, lock and unlock must be in the same database connection, otherwise the unlock will fail
Resource constraints
The execution of the program requires resources, such as database connection and bandwidth. However, due to the limitation of resources, multiple threads are serial rather than concurrent, which not only has no advantage, but also brings unnecessary context switching loss
Common Resource Constraints
- Hardware Resource Limitation
- bandwidth
- Disk read/write speed
- CPU processing speed
- Software Resource Limitation
- Database connections
- Socket connections
Coping with resource constraints
- Cluster to increase resources
- Adjust the concurrency of the application for different resource limits, find bottlenecks, increase the number of bottleneck resources, or adjust the number of threads for this bottleneck
Three ways to create a thread
Without further ado, get right to the code
Thread class inheritance
/ / Thread
class MyThread extends Thread {
// Override the run method to perform the task
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
// Call this to get the current thread
System.out.println(this.getName()+"Executed."+i); }}}public class Demo_02_02_1_ThreadCreateWays {
public static void main(String[] args) {
// Start new
MyThread myThread = new MyThread();
myThread.start();
for (int i = 0; i < 10; i++) {
// Get the current Thread through the static method of Thread
System.out.println(Thread.currentThread().getName()+"Executed."+i); }}}Copy the code
Implement Runnable
// Implement the Runnable interface
class MyThreadByRunnable implements Runnable {
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
// We can't use this
System.out.println(Thread.currentThread().getName() + "Executed."+ i); }}}public class Demo_02_02_1_ThreadCreateWays {
public static void main(String[] args) {
// Implement the Runnable interface to start threads
Thread thread = new Thread(new MyThreadByRunnable());
thread.start();
for (int i = 0; i < 10; i++) {
// Get the current Thread through the static method of Thread
System.out.println(Thread.currentThread().getName() + "Executed."+ i); }}}Copy the code
Because Runnable is a functional interface, lamba also works
new Thread(() -> {
System.out.println("Runnable is a functional interface. Lamba is also available in Java8.");
}).start();
Copy the code
Use Callable and Future
/ / use Callable
class MyThreadByCallable implements Callable<Integer> {
@Override
public Integer call(a) throws Exception {
int sum = 0;
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"Executed."+i);
sum+=i;
}
returnsum; }}public class Demo_02_02_1_ThreadCreateWays {
public static void main(String[] args) {
// Package a layer with FutureTask
FutureTask<Integer> futureTask = new FutureTask<>(new MyThreadByCallable());
new Thread(futureTask).start();
try {
// Call futureTask's get to get the returned value
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch(ExecutionException e) { e.printStackTrace(); }}}Copy the code
This is the most complicated way, it can have a return value, summarize the steps:
- Make a class implementation
Callable
Interface, overridecall
Methods,call
Perform a task - with
FutureTask
Packaging implementationCallable
An instance of an interface class - will
FutureTask
As an example ofThread
Structural parameters - call
FutureTask
The instanceget
Get the return value, call this line will block the parent thread
Callable is also a functional interface, so lamba can also be used
Why can Thread constructs contain Runnable and FutureTask? FutureTask inherits RunnableFuture, and RunnableFuture inherits Runnable and Future, so FutureTask is also Runnable
Three ways to compare
way | Ease of use | Whether task code can be shared | Whether there can be a return value | Whether an exception can be declared | Whether you can inherit from another class |
---|---|---|---|---|---|
Inheriting the Thread | simple | Can’t | Can’t | Can’t | Can’t |
Runnable | medium | can | Can’t | Can’t | can |
Callable | complex | can | can | can | can |
Inheriting Thread is the easiest, but also the least flexible
Using Callable is the most complex, but also the most flexible
Here’s an example of shared task code:
Again, the MyThreadByRunnable class above
MyThreadByRunnable myThreadByRunnable = new MyThreadByRunnable();
Thread thread = new Thread(myThreadByRunnable);
thread.start();
// If the task code is reused, Thread cannot be inherited
Thread thread2 = new Thread(myThreadByRunnable);
thread2.start();
Copy the code
Some properties of the thread
The name
Give the Thread a loud name for troubleshooting. The default is Thread-${a number}
- Set the name
threadA.setName("Welcome to follow the official wechat account 'Nobita learns programming with you'.");
Copy the code
- Get the name
threadA.getName();
Copy the code
Is it a daemon?
Threads serving other threads can be daemons, which have a feature that if all foreground threads die, the daemons die automatically.
Threads created by non-daemons default to non-daemons and those created by daemons default to daemons
- set
threadA.setDaemon(true);
Copy the code
- get
threadA.isDaemon();
Copy the code
Thread priority
A thread with a higher priority gets more CPU resources, ranging from 1 to 10. The default priority is the same as that of the parent thread that created it, with main being 5
set
threadA.setPriority(Thread.NORM_PRIORITY);
Copy the code
get
threadA.getPriority()
Copy the code
Owning thread Group
You can put threads into groups and manage them together
Setting a Thread Group
The Thread construction can be specified
ThreadGroup threadGroup = new ThreadGroup("Welcome to follow the official wechat account 'Nobita learns programming with you'.");
Thread thread = new Thread(threadGroup, () -> {
System.out.println("Welcome to follow the official wechat account 'Nobita learns programming with you'.");
});
Copy the code
Get the thread group
thread.getThreadGroup()
Copy the code
Thread group-based operations
ThreadGroup threadGroup1 = thread.getThreadGroup();
System.out.println(threadGroup1.activeCount()); // How many live threads are there
threadGroup1.interrupt(); // Interrupt all threads in the group
threadGroup1.setMaxPriority(10); // Set the highest priority of the thread
Copy the code
Thread synchronization
Multiple threads accessing the same resource can lead to uncertain results, so sometimes it is necessary to control only one thread accessing the shared resource, which is called thread synchronization.
You can use synchronized and Lock. Synchronized is also an implicit lock.
Synchronized methods
class Account {
private Integer total;
public Account(int total) {
this.total = total;
}
public synchronized void draw(int money) {
if (total >= money) {
this.total = this.total - money;
System.out.println(Thread.currentThread().getName() + "The rest" + this.total);
} else {
System.out.println(Thread.currentThread().getName() + "Not enough."); }}public synchronized int getTotal(a) {
returntotal; }}public class Demo_02_04_1_ThreadSync {
public static void main(String[] args) {
Account account = new Account(100);
Runnable runnable = new Runnable() {
@Override
public void run(a) {
while (account.getTotal() >= 10) {
account.draw(10);
try {
Thread.sleep(1);
} catch(InterruptedException e) { e.printStackTrace(); }}}}; Thread A =new Thread(runnable);
A.setName("A");
Thread B = new Thread(runnable);
B.setName("B"); A.start(); B.start(); }}Copy the code
Synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized
Sychronized applied to the normal method, the lock is the current instance object
When applied to a static method, the lock is the current Class
Synchronized code block
public void draw(int money) {
synchronized (total) {
if (total >= money) {
this.total = this.total - money;
System.out.println(Thread.currentThread().getName() + "The rest" + this.total);
} else {
System.out.println(Thread.currentThread().getName() + "Not enough."); }}}Copy the code
Synchronized Blocks that are locked as objects in ()
The lock
Lock lock = new ReentrantLock();
public void draw(int money) {
lock.lock();
try {
if (total >= money) {
this.total = this.total - money;
System.out.println(Thread.currentThread().getName() + "The rest" + this.total);
} else {
System.out.println(Thread.currentThread().getName() + "Not enough."); }}finally{ lock.unlock(); }}Copy the code
The use of relatively simple, into the method of locking, the implementation of the release, will be dedicated to the back of an article to introduce the lock, including AQS and the like, please pay attention to.
Communication between threads
The way threads work in coordination
Communication based on wait notification model
The wait/notification related methods are available to any Java Object because they are defined on java.lang.Object.
The relevant API
- Notify: Notify a thread waiting on an object to return from the wait method if the thread has acquired the lock on the object
- NotifyAll: Notifies all waiting threads on an object to return from the wait method
- Wait: to put a thread into a state of WAITING (WAITING for another thread to notify or interrupt). Note that the object lock must be released after the wait method is called
- Wait (long): Similar to wait in that a timeout period is added, and the timeout period is returned without notification
- Wait (long, int): indicates the nanosecond level
Some points to note:
- To use wait(), notify(), and notifyAll(), lock the calling object first.
- After calling wait(), the thread state changes from RUNNING to WAITING, and the current thread is placed on the object’s wait queue, releasing the lock.
- The wait thread does not return from wait() immediately after notify() or notifyAll() is called. It has to release the lock of the notify() or notifAll() thread before it has a chance to return from wait().
- The notify() method moves one thread in the wait queue from the wait queue to the synchronous queue, while the notifyAll() method moves all threads in the wait queue to the synchronous queue, and the status of the moved thread changes from WAITING to BLOCKED.
- The return from wait() is conditional upon obtaining the lock of the calling object.
About wait queues and synchronization queues
- Synchronization queue (lock pool) : Suppose thread A already owns the lock on an object (not A class), and other threads want to call A synchronized method (or block) of that object. Since these threads must acquire ownership of the lock before they can access the synchronized method of the object, However, the lock on the object is currently owned by thread A, so these threads are in the synchronized queue (lock pool) of the object, and their state is Blocked.
- Wait queue (wait pool) : If thread A calls wait() on an object, thread A releases the lock on that object (since wait() must occur in synchronized, thread A naturally owns the lock on that object before executing its wait()). At the same time, thread A enters the Waiting queue (Waiting pool) of the object, and the state of thread A is Waiting. If another thread calls notifyAll() on the same object, all the threads in the wait pool of the object are put into the synchronization queue (lock pool) of the object, ready to claim ownership of the lock. If another thread calls notify() on the same object, only one thread (at random) in the wait pool of that object will enter the synchronization queue (lock pool) of that object.
This comes from Gnawing concurrency (ii) : The life cycle of Java threads
An example of a wait notification model
class WaitNotifyModel {
Object lock = new Object();
boolean flag = false;
public void start(a) {
Thread A = new Thread(() -> {
synchronized (lock) {
while(! flag) {try {
System.out.println(Thread.currentThread().getName()+": Waiting for notice");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+ ": Received notification, process business logic"); }}); A.setName("I am the one who waits");
Thread B = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println(Thread.currentThread().getName()+": give notice"); lock.notify(); }}); B.setName("Notifier"); A.start(); B.start(); }}Copy the code
The model of induction
waiters
synchronized(object) {while{object.wait ()} handle business logic}Copy the code
notifier
synchronized(object) {change the condition object. Notify (); }Copy the code
Condition based communication
The above wait notification requires synchronized, and Condition if Lock is used
The Condition interface also provides object-like monitor methods that work with Lock to implement the wait/notify mode
The difference between Condition and Object monitors
project | Object monitor method | Condition |
---|---|---|
precondition | Get the lock of the object | The Lock. The Lock () to obtain the Lock Lock. NewCondition Condition () |
Call way | obj.wait() | condition.await() |
Number of waiting queues | a | Can be more |
The current thread releases the lock and enters the wait state | support | support |
Unresponsive interrupt in wait state | Does not support | support |
Release the lock and enter the timeout wait state | support | support |
Enter the wait state to some future time | Does not support | support |
Wakes up one or more threads in the wait | Support to notify notifyAll | Support signal signalAll |
Here are some thread states that you can look back at after the thread life cycle
The sample
The Condition object is typically used as a member variable. After calling the await() method, the current thread releases the lock and waits, while another thread calls the signal() method of the Condition object, notifying the current thread, which returns from the await() method and has acquired the lock before returning.
Implement a bounded queue that blocks the consuming thread when the queue is empty and the producing thread when the queue is full
class BoundList<T> {
private LinkedList<T> list;
private int size;
private Lock lock = new ReentrantLock();
// Take two conditions, one non-empty and one dissatisfied
private Condition notEmpty = lock.newCondition();
private Condition notFullCondition = lock.newCondition();
public BoundList(int size) {
this.size = size;
list = new LinkedList<>();
}
public void push(T x) throws InterruptedException {
lock.lock();
try {
while (list.size() >= size) {
// When it's full, wait
notFullCondition.await();
}
list.push(x);
// Wake up waiting consumers
notEmpty.signalAll();
} finally{ lock.unlock(); }}public T get(a) throws InterruptedException {
lock.lock();
try {
while (list.isEmpty()) {
// When you are free, wait
notEmpty.await();
}
T x = list.poll();
// Wake up the producer
notFullCondition.signalAll();
return x;
} finally{ lock.unlock(); }}}public class Demo_02_05_1_Condition {
public static void main(String[] args) {
BoundList<Integer> list = new BoundList<>(10);
// The thread that produces data
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
list.push(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// The thread that consumes data
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
System.out.println(list.get());
} catch(InterruptedException e) { e.printStackTrace(); } } }).start(); }}Copy the code
Thread communication based on BlockingQueue
Stay tuned for a post about BlockingQueue
Threads of control
With reference to the Crazy Java handout, the following is classified as a way to control threads.
join
If a thread joins a thread, the main thread will block until the join thread completes, and the main thread will continue to execute. If the join times out, the main thread will continue to execute without waiting for the join thread.
If the thread is alive, call wait(0). If there is a timeout, the time in the wait will be calculated
while (isAlive()) {
wait(0);
}
Copy the code
API
- public final void join() throws InterruptedException
- public final synchronized void join(long millis, int nanos)
- public final synchronized void join(long millis)
example
public class Demo_02_06_1_join extends Thread {
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName() + ""+ i); }}public static void main(String[] args) throws InterruptedException {
Demo_02_06_1_join joinThread = new Demo_02_06_1_join();
for (int i = 0; i < 100; i++) {
if (i == 10) {
joinThread.start();
joinThread.join();
}
// Stop when joinThread reaches 9, and then execute the code in joinThread
System.out.println(Thread.currentThread().getName()+""+i); }}}Copy the code
sleep
The sleep method causes the thread to pause for a period of time and enter the blocking state.
API
- public static native void sleep(long millis) throws InterruptedException
- public static void sleep(long millis, int nanos) throws InterruptedException
The sample
public class Demo_02_06_2_sleep extends Thread {
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
if (i == 5) {
try {
Thread.sleep(5000);
} catch(InterruptedException e) { e.printStackTrace(); }}// Output to 4 stop, 5 seconds later to resume
System.out.println(this.getName() + ""+ i); }}public static void main(String[] args) throws InterruptedException {
Demo_02_06_2_sleep sleepThread = newDemo_02_06_2_sleep(); sleepThread.start(); }}Copy the code
yield
Again, let the thread pause, but enter the ready state, and let the system start the scheduling process again, perhaps with luck the yield thread will be selected again next time.
Thread.yield()
Copy the code
interrupt
The Java interrupt mechanism is a cooperative mechanism, which means that interrupts do not terminate another thread directly, but the interrupted thread handles the interrupt itself.
Some of the previous methods declare InterruptedException, which means they can be interrupted, leaving the exception to the caller to handle.
The interrupted thread can handle the interrupt itself, not handle it, or throw it out.
public class Demo_02_06_3_interrupt extends Thread {
static class MyCallable implements Callable {
@Override
public Integer call(a) throws InterruptedException {
for (int i = 0; i < 5000; i++) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("3333");
throw new InterruptedException("Interrupt me why, follow wechat big male to learn programming with you."); }}return 0; }}public static void main(String[] args) throws InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
for (int i = 0; i < 100; i++) {
if (i == 3) { thread.interrupt(); }}try {
futureTask.get();
} catch (ExecutionException e) {
// An exception is caught heree.printStackTrace(); }}}Copy the code
The life cycle of a thread
Nibble concurrency (2) : The life cycle of Java threads this article is very well written, I recommend reading it.
If he had found this article earlier, He would have spared himself the trouble of reading the Art of Concurrent Programming in Java, crazy Java Handouts, and various blogs.
Here I just want to change a picture in this article to post here, you can refer to the above article for details.
But first of all, I found quite a few versions of this life cycle diagram, not only in different form, but also in different content
- There are only 5 states in crazy Java handouts, missing WAITING and TIMED_WAITING
- There are seven states in the Art of Java Concurrent Programming
- In the article above, there are seven states in the text description, but only six in the picture
Big male also meng, then found the following enumeration in the source code, there are some notes, translated.
public enum State {
// Indicates that no thread has been started
NEW,
// represents a runnable thread
// Indicates that the JVM is running, but it may need to wait for the operating system to allocate resources
/ / such as the CPU
RUNNABLE,
// indicates that the thread is waiting for a monitor lock
// Indicates that you are waiting for a monitor lock to re-enter the synchronization block or method
// Re-enter the synchronization block OR method after calling Object.wait
BLOCKED,
Calling one of the following methods enters WAITING
// 1.object.wait () does not add a timeout parameter
// 2. Call join() without a timeout parameter
// 3. Call locksupport.park ()
// A thread in WAITING state is WAITING for another thread to do a special action
// 1. Wait for other threads to invoke notify or notifyAll
// 2. The join is waiting for the specified thread to terminate
WAITING,
// a thread that has a specific wait time
Call the following method with a specified positive timeout to get into this state
// 1. Thread.sleep
// 2. Thread.join(long)
// 3. LockSupport.parkNanos
// 4. LockSupport.parkUntil
TIMED_WAITING,
// The execution is complete
TERMINATED;
}
Copy the code
For a person who has cet-6 and cet-8, the translation is too difficult, but it feels clear.
There should be 7 states!!
Instead of studying the flow of state in detail, Nobita made an unprecedented thread lifecycle diagram by referring directly to some materials and the above translation
This diagram is probably, maybe, probably not too much problem. In this figure, the forgive color is the thread state, and purple is the cause of the state change.
ThereadLocal
It’s a place to store things that’s bound to the thread.
Use the sample
class Profiler {
// Create a ThreadLocal
private static ThreadLocal<Long> threadLocal = new ThreadLocal<Long>(){
@Override
protected Long initialValue(a) {
returnSystem.currentTimeMillis(); }};// Record the start time
public static void begin(a) {
threadLocal.set(System.currentTimeMillis());
}
// Record time
public static Long end(a) {
returnSystem.currentTimeMillis() - threadLocal.get(); }}public class Demo_02_08_1_ThreadLocal {
public static void main(String[] args) {
new Thread(() -> {
Profiler.begin();
long sum = 1;
for (int i = 1; i < 20; i++) {
sum*=i;
}
System.out.println(sum);
System.out.println(Thread.currentThread().getName()+"Take ="+Profiler.end());
}).start();
new Thread(() -> {
Profiler.begin();
int sum = 1;
for (int i = 1; i < 1000; i++) {
sum+=i;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(sum);
System.out.println(Thread.currentThread().getName()+"Take ="+Profiler.end()); }).start(); }}Copy the code
InheritableThreadLocal
This type of ThreadLocal can be passed from the parent thread to the child thread, meaning that the child thread can access the InheritableThreadLocal in the parent thread
public class Demo_02_08_2_ThreadLocalInherit {
static class TestThreadLocalInherit extends Thread{
@Override
public void run(a) {
System.out.println(threadLocal.get()); // null
System.out.println(inheritableThreadLocal.get()); // Welcome to follow wechat public number Nobita and learn programming with you}}public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
public static InheritableThreadLocal<Object> inheritableThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
inheritableThreadLocal.set("Welcome to follow the wechat public number Nobita and learn programming with you.");
threadLocal.set("ddd");
newTestThreadLocalInherit().start(); }}Copy the code
Realize the principle of
It’s easy to assume that because this object follows the Thread, it should be an attribute of the Thread. In fact, ThreadLocal and InheritableThreadLocal are stored in the Thread.
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
Copy the code
These are the two member variables of Thread. They are of the same type.
ThreadLocalMap is an inner class of ThreadLocal that stores data in an array of entries. When you set a ThreadLocal as the key, you pass in the value that you want to store, and it hashes the key, builds an Entry, and puts it in an Entry array.
/ / pseudo-code
static class ThreadLocalMap {
// Internal Entry structure
static class Entry {... }// Save data
private Entry[] table;
// set
private void set(ThreadLocal
key, Object value) {
int i = key.threadLocalHashCode & (len-1);
tab[i] = new Entry(key, value);
}
// get
private Entry getEntry(ThreadLocal
key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if(e ! =null && e.get() == key)
return e;
else
returngetEntryAfterMiss(key, i, e); }}Copy the code
Take a look at ThreadLocal’s get method
public T get(a) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); // This is the function for Thread locals
if(map ! =null) {
// Here is the easy part
ThreadLocalMap.Entry e = map.getEntry(this);
if(e ! =null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
returnresult; }}// This is also very simple. It calls your overridden initialValue method, takes a value, sets it in and returns it to you
// This is also interesting. Normally init is done after initialization, but it is called when you fetch it
return setInitialValue();
}
Copy the code
Let’s take a look at ThreadLocal’s set, which is super simple
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if(map ! =null)
map.set(this, value);
else
createMap(t, value);
}
Copy the code
If you’re done with ThreadLocal, look at InheritableThreadLocals and see how it can take things from the parent thread
// Inherit ThreadLocal and override three methods
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/ / this method is directly throw an exception UnsupportedOperationException in ThreadLocal
protected T childValue(T parentValue) {
return parentValue;
}
// 超简单,我们的Map不要threadLocals了,改为inheritableThreadLocals
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/ / same as above
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); }}Copy the code
How does inheritableThreadLocals deliver this function
Search for inheritableThreadLocals directly within the Thread
You’ll notice that it is assigned in init, which is called in the Thread constructor
// The parent thread is the thread that created the parent thread
if(inheritThreadLocals && parent.inheritableThreadLocals ! =null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
Copy the code
Now have to look at a ThreadLocal. CreateInheritedMap this method
ParentMap is the inheritableThreadLocals of the parent thread
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
// Add elements from the parent thread to the inheritableThreadLocals component of your own thread
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if(e ! =null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if(key ! =null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while(table[h] ! =null) h = nextIndex(h, len); table[h] = c; size++; }}}}Copy the code
To summarize
ThreadLocal and InheritableThreadLocal are implemented based on two variables inside the Thread that are similar to a HashMap structure called ThreadLocalMap, The Entry key is ThreadLocal and value is the value you saved. InheritableThreadLocal is implemented primarily when the thread is created, and if the parent thread has an InheritableThreadLocal, it is copied to the child thread.
Atomic classes
A simple I ++ operation, which is not atomic in a multithreaded environment if I is shared.
. To this end, Java. Util. Concurrent atomic offers some atomic classes at the bottom of the bag, these atomic operation provides a highly efficient, usage simple, the performance thread safe to update a variable way.
An example of use
public class Demo_04_01_1_Atomic {
static class Counter {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public int increment(a) {
return atomicInteger.getAndIncrement();
}
public int get(a) {
returnatomicInteger.get(); }}static class Counter2 {
private int value = 0;
public int increment(a) {
return value++;
}
public int get(a) {
returnvalue; }}public static void main(String[] args) throws InterruptedException {
// This uses the atom class
Counter counter = new Counter();
// This does not use atomic classes
Counter2 counter2 = new Counter2();
for (int i = 0; i < 50; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
counter.increment();
counter2.increment();
}
}).start();
}
Thread.sleep(2000);
System.out.println(counter.get()); // It must be 5000
System.out.println(counter2.get()); // May be less than 5000}}Copy the code
Super simple ~
The implementation of the atomic class is CAS
Chapter summary
The source file can be found under the Github Java-concurrent-Programming -art-mini chapter
reference
- Gnawing concurrency (II) : The life cycle of Java threads
The related resources
- Gitbook notes (looks comfortable, can’t access the next one)
- Gitbook notes (not as good as the last one)
- Gitbook Github address of notes