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:

  1. Make a class implementationCallableInterface, overridecallMethods,callPerform a task
  2. withFutureTaskPackaging implementationCallableAn instance of an interface class
  3. willFutureTaskAs an example ofThreadStructural parameters
  4. callFutureTaskThe instancegetGet 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