Click on the top of the “programmer xiao Le” attention, star label or top grow together

Every morning 00 00, the first time to meet you


Daily English

Sometimes,you are not happy if you see through everything.It’s better to be naive and inattentive.

Most of the time, see too thoroughly but not happy, rather naive heartless.

Open your heart every day

Life, why coy. Life, why be timid. Tired, listen to music; Sad, kan mood; Failure, start all over again; Life, need is casual.

From: Oo LuYi Oo | coordinating editor: le le

Link: juejin. Im/post / 5 e0d8765f265da5d332cde44

Programmer Le (ID: Study_tech) tweet # 746 from Pexels


Past review: The Internet situation is not optimistic, zhou Hongyi, chairman of qihoo 360, will send a “no-layoff card” to reassure the army

The body of the

1. Usage scenarios of ThreadLocal

1.1 scenario 1

Each thread needs a proprietary object (usually a utility class, typically SimpleDateFormat and Random)

Each Thread has its own instance copy, which is not shared

Metaphor: teaching material has only one, make notes together have thread safety problem. No problem after photocopying, using ThradLocal is equivalent to photocopying the textbook.

One story illustrates the private domain of threads: ThreadLocal

Understand ThreadLocal in depth (these details should not be overlooked)

ThreadLocal

1.2 scenario 2

The need to hold global variables within each thread (such as retrieving user information in an interceptor) allows different methods to use them directly, avoiding the hassle of passing parameters

2. Practice the above scenarios

2.1 Practice Scenario 1

/** * Two threads print dates */ public class ThreadLocalNormalUsage00 {public static void main(String[] args) throws InterruptedException { new Thread(newRunnable() {
            @Override
            public void run() {
                String date = new ThreadLocalNormalUsage00().date(10);
                System.out.println(date);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() { String date = new ThreadLocalNormalUsage00().date(104707); System.out.println(date); } }).start(); } public String date(int seconds) {public String date(int seconds) {date date = new date(1000 * seconds); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        returndateFormat.format(date); }}Copy the code

The results

Because China is located in the 8th east district, the time is calculated from 8 o ‘clock on January 1, 1970

Public class ThreadLocalNormalUsage01 {public static void main(String[] args) throws public class ThreadLocalNormalUsage01 {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(new Runnable() {
                @Override
                public void run() { String date = new ThreadLocalNormalUsage01().date(finalI); System.out.println(date); } }).start(); Thread.sleep(100); }} public String date(int seconds) {date date = new date(1000 * seconds); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        returndateFormat.format(date); }}Copy the code

The results

Multiple threads print their own times (performance issues can arise if there are too many threads), so use thread pools.

/** * 1000 threads print dates, Executables */ public class ThreadLocalNormalUsage02 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10); public static void main(String[] args) throws InterruptedException {for(int i = 0; i < 1000; i++) { int finalI = i; // Submit the task threadPool.submit(newRunnable() {
                @Override
                public void run() { String date = new ThreadLocalNormalUsage02().date(finalI); System.out.println(date); }}); } threadPool.shutdown(); } public String date(int seconds) {public String date(int seconds) {date date = new date(1000 * seconds); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        returndateFormat.format(date); }}Copy the code

The results

But when you use a thread pool, you’ll find that each thread has its own SimpleDateFormat object, which isn’t necessary, so declare SimpleDateFormat static, ensuring that there is only one

/** * 1000 threads print the date, using the thread pool to execute, */ public class ThreadLocalNormalUsage03 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10); Static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    public static void main(String[] args) throws InterruptedException {
        for(int i = 0; i < 1000; i++) { int finalI = i; // Submit the task threadPool.submit(newRunnable() {
                @Override
                public void run() { String date = new ThreadLocalNormalUsage03().date(finalI); System.out.println(date); }}); } threadPool.shutdown(); } public String date(int seconds) {public String date(int seconds) {date date = new date(1000 * seconds);returndateFormat.format(date); }}Copy the code

The results

A print with the same number of seconds appears, which is clearly incorrect.

The cause of the problem

Multiple threaded tasks point to the same SimpleDateFormat object. SimpleDateFormat is not thread-safe.

A solution to the problem

Solution 1: Lock

Return dateformat.format (date); So you can add a synchronized lock to the last line of code

Public String date(int seconds) {// The parameter is in milliseconds, starting from 1970.1.1 00:00:00 GMT date date = new date(1000 * seconds); String s; synchronized (ThreadLocalNormalUsage04.class) { s = dateFormat.format(date); }return s;
}
Copy the code

The results

The same time is not found in the run result, achieving the purpose of thread safety

Cons: With the addition of synchronized, only one thread can execute at a time, which is definitely not a good option in high-concurrency scenarios, so look for alternatives.

Scenario 2: Use ThreadLocal

/** * Use ThreadLocal to assign each thread its own dateFormat object. Public class ThreadLocalNormalUsage05 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10); public static void main(String[] args) throws InterruptedException {for(int i = 0; i < 1000; i++) { int finalI = i; // Submit the task threadPool.submit(newRunnable() {
                @Override
                public void run() { String date = new ThreadLocalNormalUsage05().date(finalI); System.out.println(date); }}); } threadPool.shutdown(); } public String date(int seconds) {public String date(int seconds) {date date = new date(1000 * seconds); / / get a SimpleDateFormat object SimpleDateFormat dateFormat = ThreadSafeFormatter. DateFormatThreadLocal. The get ();returndateFormat.format(date); } } class ThreadSafeFormatter { public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>(){// Create a SimpleDateFormat objectinitialValue() {
            return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); }}; }Copy the code

The results

With ThreadLocal, different threads will not share SimpleDateFormat objects, so there will be no thread-safety issues

2.2 Practice Scenario 2

The current user information needs to be shared by all methods in the thread

Scheme 1: Pass parameters

You can pass user as a parameter in each method,

Disadvantages: This can lead to code redundancy and poor maintainability.

Solution 2: Use Map

One way to improve on this is to use a Map that stores the information in the first method and then uses direct GET (),

Disadvantages: If it is safe in a single-threaded environment, it is not safe in a multi-threaded environment. Both locking and ConcurrentHashMap can cause performance problems if used.

Solution 3: Use ThreadLocal to share resources between different methods

Using ThreadLocal avoids the performance issues associated with locking and avoids passing parameters across layers to fulfill business requirements, allowing different threads to store different information.

/** * Public class ThreadLocalNormalUsage06 {public static void main(String[] args) {new Service1().process(); } } class Service1 { public voidprocess() {
        User user = new User("LuYi"); / / will be stored in the User object holder UserContextHolder. Holder. Set (User); new Service2().process(); } } class Service2 { public voidprocess() {
        User user = UserContextHolder.holder.get();
        System.out.println("Service2 gets username:" + user.name);
        new Service3().process();
    }
}

class Service3 {

    public void process() {
        User user = UserContextHolder.holder.get();
        System.out.println("Service3 gets username:"+ user.name); } } class UserContextHolder { public static ThreadLocal<User> holder = new ThreadLocal<>(); } class User { String name; public User(String name) { this.name = name; }}Copy the code

The results

3. Summary of ThreadLocal

  • Separate threads from each other (each thread has its own separate object)

  • This object can be easily obtained in any method

  • Choose whether to use the initialValue method or the set method depending on when the shared object is generated

  • InitialValue is used when we control the timing of object initialization

  • Use set if the timing of object generation is out of our control

4. Benefits of using ThreadLocal

  • Achieve thread safety

  • No need to lock, high execution efficiency

  • More memory saving, save overhead

  • Avoid the cumbersome parameter transmission, reduce the code coupling degree

5. ThreadLocal principle

  • Thread

  • ThreadLocal

  • ThreadLocalMap

Within the Thread class have ThreadLocal. ThreadLocalMap threadLocals = null; This variable is used to store ThreadLocal’s. Since there can be multiple ThreadLocal’s in the same thread and get() is called multiple times, a ThreadLocalMap needs to be maintained internally to store multiple ThreadLocal’s

5.1 Methods related to ThreadLocal

T initialValue()

This method is used to set the initial value and is only triggered when the get() method is called, so it is lazy loading.

But if the set() operation is done before get(), then initialValue() is not called.

Normally this method can only be called once per thread, but can be called again after removing () is called

public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); // Returns the value directly to resuleif(map ! = null) { ThreadLocalMap.Entry e = map.getEntry(this);if(e ! = null) { @SuppressWarnings("unchecked")
            T result = (T)e.value;
            returnresult; }} // It will be initialized if it is not retrievedreturn setInitialValue();
}

private T setInitialValue() {// Get the value generated by initialValue and do so in subsequent operationssetT value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);if(map ! = null) map.set(this, value);else
        createMap(t, value);
    return value;
}

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if(m ! = null) m.remove(this); }Copy the code

void set(T t)

Set a new value for this thread

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

T get()

Gets the value of the thread

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if(map ! = null) { ThreadLocalMap.Entry e = map.getEntry(this);if(e ! = null) { @SuppressWarnings("unchecked")
            T result = (T)e.value;
            returnresult; }}return setInitialValue();
}
Copy the code

void remove()

Delete the value corresponding to this thread

6. ThreadLocal pay attention to the point

6.1 Memory Leakage

Memory leak; An object can no longer be used, but its memory cannot be reclaimed

static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) {// Call the parent class, which is a weak reference to super(k); // strong reference value = v; }}Copy the code

Strong references: When GC is triggered when out of memory, throw OOM rather than reclaim strongly referenced memory

Weak references: Memory for weak references is reclaimed after GC is triggered

normal

When Thread finishes running, the value in ThreadLocal is reclaimed because there are no strong references left

Abnormal condition

ThreadLocalMap–>Entry(key null)–>value (value null)–>value Therefore, if value cannot be reclaimed, OOM may appear.

The JDK design takes this into account, so the set(), remove(), and resize() methods scan for an Entry with a null key and set the corresponding value to NULL so that the value object can be reclaimed.

private void resize() {
    Entry[] oldTab = table;
    int oldLen = oldTab.length;
    int newLen = oldLen * 2;
    Entry[] newTab = new Entry[newLen];
    int count = 0;

    for (int j = 0; j < oldLen; ++j) {
        Entry e = oldTab[j];
        if(e ! = null) { ThreadLocal<? > k = e.get(); // If ThreadLocal is null, set the corresponding value of ThreadLocal to nullif (k == null) {
                e.value = null; // Help the GC
            } else {
                int h = k.threadLocalHashCode & (newLen - 1);
                while(newTab[h] ! = null) h = nextIndex(h, newLen); newTab[h] = e; count++; }}}setThreshold(newLen);
    size = count;
    table = newTab;
}
Copy the code

But these operations occur only when the set(), remove(), resize() methods are called. If these methods are not called and the thread does not stop, then the call chain will always exist, so memory leaks can occur.

6.2 How to Avoid Memory Leaks (Ali Protocol)

Calling the remove() method removes the corresponding Entry object to avoid memory leaks, so call the remove() method after using ThreadLocal.

class Service1 {

    public void process() {
        User user = new User("LuYi"); / / will be stored in the User object holder UserContextHolder. Holder. Set (User); new Service2().process(); } } class Service2 { public voidprocess() {
        User user = UserContextHolder.holder.get();
        System.out.println("Service2 gets username:" + user.name);
        new Service3().process();
    }
}

class Service3 {

    public void process() {
        User user = UserContextHolder.holder.get();
        System.out.println("Service3 gets username:"+ user.name); / / manual release the memory, thus to avoid memory leaks UserContextHolder. Holder. The remove (); }}Copy the code

6.3 Null Pointers of ThreadLocal are Abnormal

Public class ThreadLocalNPE {ThreadLocal<Long> longThreadLocal = new ThreadLocal<>(); public class ThreadLocalNPE {ThreadLocal<Long> longThreadLocal = new ThreadLocal<>(); public voidset() {
        longThreadLocal.set(Thread.currentThread().getId());
    }

    public Long get() {
        returnlongThreadLocal.get(); } public static void main(String[] args) { ThreadLocalNPE threadLocalNPE = new ThreadLocalNPE(); System.out.println(threadLocalpe. Get ())); Thread thread1 = new Thread(newRunnable() {
            @Override
            public void run() { threadLocalNPE.set(); System.out.println(threadLocalNPE.get()); }}); thread1.start(); }}Copy the code

6.4 Troubleshooting the null pointer exception

A null pointer exception is reported if the get method returns a primitive type, but not if the get method returns a wrapper type. This is because there is a boxing and unboxing relationship between the basic type and the packaging type, causing the null pointer problem is due to the user.

6.5 Shared Object Problem

If the object threadlocal-set () enters in each thread is the same object shared by multiple threads, such as a static object, then multiple threads calling threadlocal-get () will still fetch the same object, and thread-safety issues will still occur.

6.6 Don’t force ThreadLocal if you can’t use it

When the number of tasks is small, creating objects in local methods solves the problem, eliminating the need for ThreadLocal.

6.7 use framework support in preference to creating your own

In the Spring framework, for example, if you can use the RequestContextHolder, you don’t need to maintain your own ThreadLocal because you might forget to call the remove() method and so on, causing a memory leak.

Feel free to leave your opinion in the comments section and discuss improvements together. If today’s article gives you new inspiration and new understanding on the improvement of learning ability, please share it with more people.

Welcome to join the programmer xiaole technology group, in the public number back to “add group” or “learn”.

Guess you want to see more

Alibaba, Tencent, Baidu, Huawei, JINGdong latest interview questions collection

How many threads is a reasonable number for a thread pool? (Article to help you understand)

JVM memory structure parsing, look at all good!

IDEA of this several debugging SAO operation, with all said cool!

Check out "Programmer Le" for more highlightsCopy the code

Hey, are you watching?