ThreadLocal Java Fundamentals -

A .ThreadLocal variable definitions:
ThreadLocal Java, is a special kind of variable. Each thread has a ThreadLocal that each thread has its own independent variable, the conditions of competition was completely eliminated. If you provide a copy of their own unique variables for each thread, will greatly improve efficiency. First, by multiplexing reduces the number of costly creation of objects. Secondly, you do not get to use a thread-safe case of synchronous or invariance costly.

The two .API explained:
This class provides thread-local (thread-local) variables. These variables differ from their normal counterparts, because access to a variable (through which get or set method) Each thread has its own local variable, which is independent of the initialization copy of the variable. ThreadLocal instance private static fields are typically class, and they wish to state one thread (e.g., user ID or transaction ID) is associated.
For example, the following class generating a unique local identifier for each thread. The thread ID is assigned when the first call UniqueThreadIdGenerator.getCurrentThreadId (), does not change in subsequent calls.

  import java.util.concurrent.atomic.AtomicInteger;

 public class UniqueThreadIdGenerator {

     private static final AtomicInteger uniqueId = new AtomicInteger(0);

     private static final ThreadLocal < Integer > uniqueNum = 
         new ThreadLocal < Integer > () {
             @Override protected Integer initialValue() {
                 return uniqueId.getAndIncrement();
         }
     };
 
     public static int getCurrentThreadId() {
         return uniqueId.get();
     }
 } // UniqueThreadIdGenerator

Each thread has maintained an implicit reference to its copy of a thread-local variables, as long as the thread is alive and the ThreadLocal instance is accessible; after thread disappear, all copies will be the local instance of its thread garbage collection (unless the existence of these other references to copy).
Here Insert Picture Description
III. Let's look at an example through the use of ThreadLocal

public class MyThreadLocalStringDemo {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    private String getString() {
        return threadLocal.get();
    }

    private void setString(String string) {
        threadLocal.set(string);
    }

    public static void main(String[] args) {
        int threads = 9;
        MyThreadLocalStringDemo demo = new MyThreadLocalStringDemo();
        CountDownLatch countDownLatch = new CountDownLatch(threads);
        for (int i = 0; i < threads; i++) {
            Thread thread = new Thread(() -> {
                demo.setString(Thread.currentThread().getName());
                System.out.println(demo.getString());
                countDownLatch.countDown();
            }, "thread - " + i);
            thread.start();
        }
    }
}

Results are as follows:
Here Insert Picture Description
There may be some people feel in case we can to achieve this through the lock. Yes, yes, indeed lock can solve this problem, but here we emphasize that thread data isolate the problem, not the problem of multi-threaded shared data. If we here in addition to getString () than there are many other methods also used this String, this time there is no explicit data transfer process between the various methods, and can be directly acquired in ThreadLocal variable, this is the ThreadLocal the core thread data sharing the same data a different thread isolation. Since ThreadLocal support generics, here is store a String to demonstrate, in fact, can store any type, the effect is the same.

Four .ThreadLocal source code analysis
prior to analysis source code we understand a thing that is the object instance mappings and ThreadLocal variables by threads Thread to maintain , in other words, the object instance mappings ThreadLocal variable and is stored inside a Map ( Map the Map is not an abstract Map in java.util), and this Map is a field of the Thread class ! But the real storage mappings Map is ThreadLocalMap . Here we look at the specific source is achieved by several methods of view.

//set 方法	
public void set(T value) {	
    Thread t = Thread.currentThread();	
    ThreadLocalMap map = getMap(t);	
    if (map != null)	
        map.set(this, value);	
    else	
        createMap(t, value);	
}	
	
//获取线程中的ThreadLocalMap 字段!!	
ThreadLocalMap getMap(Thread t) {	
    return t.threadLocals;	
}	
	
//创建线程的变量	
void createMap(Thread t, T firstValue) {	
     t.threadLocals = new ThreadLocalMap(this, firstValue);	
}	

First, get set method in the current thread, and then get through the current thread ThreadLocalMap getMap to a variable of type threadLocals, if there is the direct assignment, if the thread does not exist to create ThreadLocalMap variable and assignment. When the assignment of this variable here is to call the object instance itself.


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;	
            return result;	
        }	
    }	
    return setInitialValue();	
}	
	
	
private T setInitialValue() {	
    T value = initialValue();	
    Thread t = Thread.currentThread();	
    ThreadLocalMap map = getMap(t);	
    if (map != null)	
        map.set(this, value);	
    else	
        createMap(t, value);	
    return value;	
}	

get method is relatively simple, is also the first to obtain ThreadLocalMap variable current thread, if there is the return value, does not exist, create and returns the initial value.

ThreadLocalMap using the Entry [] array to store the relationship between object instances with variables and instance objects as Key , variable as the value to achieve correspondence relationship. And the key here is used in a weak reference to the object instance, (we are here is because the key object instance, each object instance has its own life cycle, using weak references can be here without affecting the object instance in the life cycle reference to them). This part of the source code is not posted.

Five practical use (the interview can answer)
Scene: high concurrency, SimpleDateFormat is not thread-safe, we use ThreadLocal to address security thread.
Solution:
4, using ThreadLocal: Each thread has its own SimpleDateFormat object. (Recommended Use)

public class DateUtils {
    /**
     * 锁对象
     */
    private static final Object lockObj = new Object();
    /**
     * 存放不同的日期模板格式的sdf的Map
     */
    private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
    /**
     * 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
     *
     * @param pattern
     * @return
     */
    private static SimpleDateFormat getSdf(final String pattern) {
        ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);

        // 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
        if (tl == null) {
            synchronized (lockObj) {
                tl = sdfMap.get(pattern);
                if (tl == null) {
                    // 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
                    System.out.println("put new sdf of pattern " + pattern + " to map");
                    // 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
                    tl = new ThreadLocal<SimpleDateFormat>() {
                        @Override
                        protected SimpleDateFormat initialValue() {
                            System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
                            return new SimpleDateFormat(pattern);
                        }
                    };
                    sdfMap.put(pattern, tl);
                }
            }
        }
        return tl.get();
    }
    /**
     * 使用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
     * 如果新的线程中没有SimpleDateFormat,才会new一个
     * @param date
     * @param pattern
     * @return
     */
    public static String format(Date date, String pattern) {
        return getSdf(pattern).format(date);
    }
    public static Date parse(String dateStr, String pattern) throws Exception {
        return getSdf(pattern).parse(dateStr);
    }
}

Test code:

public class DateUtilTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                DateUtils.format(new Date(), "yyyy-MM-dd");
            };
        };
        Thread t2 = new Thread() {
            @Override
            public void run() {
                DateUtils.format(new Date(), "yyyy-MM-dd");
            };
        };
        Thread t3 = new Thread() {
            @Override
            public void run() {
                DateUtils.format(new Date(), "yyyy-MM-dd");
            };
        };
        Thread t4 = new Thread() {
            @Override
            public void run() {
                try {
                    DateUtils.parse("2017-06-10 12:00:01", "yyyy-MM-dd HH:mm:ss");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        };
        Thread t5 = new Thread() {
            @Override
            public void run() {
                try {
                    DateUtils.parse("2017-06-10 12:00:01", "yyyy-MM-dd HH:mm:ss");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        };
        System.out.println("单线程执行:");
        ExecutorService exec1 = Executors.newFixedThreadPool(1);
        exec1.execute(t1);
        exec1.execute(t2);
        exec1.execute(t3);
        exec1.execute(t4);
        exec1.execute(t5);
        exec1.shutdown();

        Thread.sleep(1000);

        System.out.println("双线程执行:");
        ExecutorService exec2 = Executors.newFixedThreadPool(2);
        exec2.execute(t1);
        exec2.execute(t2);
        exec2.execute(t3);
        exec2.execute(t4);
        exec2.execute(t5);
        exec2.shutdown();
    }
}

Results are as follows:
Here Insert Picture Description
1) a thread performs five tasks, new two SimpleDateFormat objects
2) two threads to perform five tasks, new four SimpleDateFormat objects, each thread has two format "yyyy-MM- dd "," yyyy-mM- dd HH: mm: ss " object, no shared SimpleDateFormat objects between threads, for each thread, are linear actuators, the phenomenon does not appear to share the Calendar. It is the perfect solution to the problem SimpleDateFormat thread safe.
If you use the first method, perform five tasks, certainly need a new five SimpleDateFormat, the concept for a single thread does not reuse. But using ThreadLocal, for a single thread, the same format that can be reused SimpleDateFormat object, so this way, both to ensure thread-safe, they can not consume too many system resources, in fact, this idea and web request, response object is the same as , it is isolated by a thread, each thread to maintain a copy of your objects to ensure thread safety.

Published 99 original articles · won praise 2 · Views 2613

Guess you like

Origin blog.csdn.net/weixin_41588751/article/details/105231421