Basic use of thread entry

  • Error locking and cause analysis: Synchronized is the object of the lock, must ensure that the lock object can not change

    • Important function: identityHashCode

      • What it does: Blocks overrides of hashCode()

        • HashCode is a member method in Object that can be overridden by any Java class,
        • Calling identityashCode returns an unoverwritten hashCode, even if hashCode() is overwritten
        • A hashCode can be thought of as a unique identifier for an object, calculated by taking the location of the object in memory (hash collisions)
      • Source:

         public static native int identityHashCode(Object x);
        Copy the code
    • Why is the hashCode of the member variable I (locked) changed after I ++

      • Code display:

        package cn.enjoyedu.ch1.syn; /** * */ public class TestIntegerSyn {public static void main(String[] args) throws InterruptedException {Worker worker=new Worker(1); //Thread.sleep(50); for(int i=0; i<5; i++) { new Thread(worker).start(); } } private static class Worker implements Runnable{ private Integer i; public Worker(Integer i) { this.i=i; } @Override public void run() { synchronized (i) { Thread thread=Thread.currentThread(); System.out.println(thread.getName()+"--@"+ System.identityHashCode(i)); // mask hashCode overwriting (this is the address of the object in memory, excluding hash collisions) i++; System.out.println(thread.getName()+"-------"+i+"-@"+ System.identityHashCode(i)); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(thread.getName()+"-------"+i+"--@"+ System.identityHashCode(i)); }}}}Copy the code
      • Running results:

      • Look for reasons:

        1. Get the program’s.class file and call a decompiler to look at it

          I++ becomes integer.valueof (this.i.ntvalue () + 1);

        2. Look at integer.valueof: new objects will be created when i++ is created

           public static Integer valueOf(int i) {
               if (i >= IntegerCache.low && i <= IntegerCache.high)
                   return IntegerCache.cache[i + (-IntegerCache.low)];
               return new Integer(i);
           }
          Copy the code
        3. How to solve synchronized(I) problem?

          1. This lock

          2. Instantiation Object

            Object o = new Object(); ... Synchronized (o){// synchronize the contents of the code block}Copy the code
  • The volatile keyword

    • Overview: Lightest synchronization mechanism to ensure visibility

      • Visibility: When a thread changes the value of a volatile variable, the new value is guaranteed to be seen by other threads

        • I don’t know if I can see it without synchronization
    • Visibility verification:

      • Code: without the volatile keyword

        package cn.enjoyedu.ch1.vola; import cn.enjoyedu.tools.SleepTools; Public class VolatileCase {private static Boolean ready; VolatileCase {VolatileCase, VolatileCase, VolatileCase, VolatileCase, VolatileCase, VolatileCase, VolatileCase; private static int number; private static class PrintThread extends Thread{ @Override public void run() { System.out.println("PrintThread is running......." ); while(! ready); System.out.println("number = "+number); } } public static void main(String[] args) { new PrintThread().start(); SleepTools.second(1); number = 51; ready = true; SleepTools.second(5); System.out.println("main is ended!" ); }}Copy the code
      • Run screenshots: the main thread is finished, the child thread is still in an infinite loop, proving that the main thread modified variables are visible in the child thread

      • Code: Add the volatile keyword

        package cn.enjoyedu.ch1.vola; import cn.enjoyedu.tools.SleepTools; Public class VolatileCase {private Volatile static Boolean ready; VolatileCase {private VolatileCase static Boolean ready; private static int number; private static class PrintThread extends Thread{ @Override public void run() { System.out.println("PrintThread is running......." ); while(! ready); System.out.println("number = "+number); } } public static void main(String[] args) { new PrintThread().start(); SleepTools.second(1); number = 51; ready = true; SleepTools.second(5); System.out.println("main is ended!" ); }}Copy the code
      • The child thread has exited the loop, proving that the variables modified by the main thread are visible in the child thread

    • Can volatile replace the synchronized keyword?

      • No, volatile only guarantees visibility, not atomicity (concurrency safety of data in a multithreaded environment)

      • Application scenario: Read more than write

        • Only one thread writes the variable, and multiple threads read the variable

ThreadLocal: Provides security for multiple threads

  • Differences between ThreadLocal and synchronized:

    • Synchronized: uses the locking mechanism to ensure that only one thread can access a code or variable at a time in a multi-threaded environment

    • ThreadLocal: provides a copy of variables for each thread (managing only its own)

      • Thread isolation is implemented: each thread accesses only its own data to ensure security

      • This ThreadLocal is used in transactions in Spring

        • Each thread saves its own link, which is tied to the thread as a parameter
  • Using the details

    1. You can initialize assignments to ThreadLocal
    2. You can use get/set/remove
  • Verify that ThreadLocal provides a copy of the variable for each thread

    • Code:

      package cn.enjoyedu.ch1.threadlocal; /** * Public class UseThreadLocal {//TODO private static ThreadLocal<Integer> ThreadLocal = new ThreadLocal<Integer>(){Override protected Integer initialValue() {return 1; }}; / / public void StartThreadArray(){Thread[] runs = new Thread[3]; / / Public void StartThreadArray(){Thread[] runs = new Thread[3]; for(int i=0; i<runs.length; i++){ runs[i]=new Thread(new TestThread(i)); } for(int i=0; i<runs.length; i++){ runs[i].start(); }} /** * */ public static class implements Runnable{int id; public static class implements Runnable; public TestThread(int id){ this.id = id; } public void run() { System.out.println(Thread.currentThread().getName()+":start"); //TODO Integer s = threadLocal.get(); // get s = s+id; threadLocal.set(s); System.out.println(thread.currentThread ().getName()+":"+ +threadLocal.get()); } } public static void main(String[] args){ UseThreadLocal test = new UseThreadLocal(); test.StartThreadArray(); }}Copy the code
    • Run screenshot:

  • Implementation of ThreadLocal: Map(current thread,Object)

    • How it works: Save a copy for each variable

      • When a new thread comes in, create a unique (static) ThreadLocalMap(ThreadLocal,object) for it // Object is the thing to save

      • Schematic diagram:

    • The get method:

      • Execution process:

        1. Get current thread
        2. The current thread is passed to getMap
      • The source code:

        public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); ThreadLocalMap (map! Threadlocalmap. entry e = map.getentry (this); if (e ! = null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; }}Copy the code
      • GetMap: Returns a threadLocals for the current thread

         ThreadLocalMap getMap(Thread t) {
             return t.threadLocals;
         }
        Copy the code
      • ThreadLocals: member variable ThreadLocalMap of ThreadLocal

         ThreadLocal.ThreadLocalMap threadLocals = null;
        Copy the code
      • ThreadLocalMap(each ThreadLocal corresponds to a unique ThreadLocalMap) : Built-in Entry<ThreadLocal <? > k,Object v), and Entry is an array with an initial size of 16 (since it is possible to define multiple ThreadLocal<? >)

      • When the map is not empty, get the array of entries in the map (by bit operation)

        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 return getEntryAfterMiss(key, i, e); }Copy the code
    • Set: Resolves hash conflicts internally

  • ThreadLocal memory leak analysis:

    • Use JDK tools to check Java process memory changes:

      • Code:

        package cn.enjoyedu.ch1.threadlocal; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; Public class ThreadLocalOOM {private static final int TASK_LOOP_SIZE = 500; private static final int TASK_LOOP_SIZE = 500; Final static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 5, 1, timeUnit.minutes, new LinkedBlockingQueue<>()); static class LocalVariable { private byte[] a = new byte[1024*1024*5]; Final static ThreadLocal<LocalVariable> LocalVariable = new ThreadLocal<>(); // In each Runnable task simply new LocalVariable, Public static void main(String[] args) throws InterruptedException {for (int I = 0; i < TASK_LOOP_SIZE; ++i) { poolExecutor.execute(new Runnable() { public void run() { new LocalVariable(); System.out.println("use local varaible"); }}); Thread.sleep(100); } System.out.println("pool execute over"); }}Copy the code
      • Memory analysis: Observed continuous heap memory changes of 25MB (five threads running at the same time, each thread allocated 5MB)

      • Change code: Introduce ThreadLocal, which holds a copy of a variable for each thread

        public static void main(String[] args) throws InterruptedException { for (int i = 0; i < TASK_LOOP_SIZE; ++i) { poolExecutor.execute(new Runnable() { public void run() { localVariable.set(new LocalVariable()); System.out.println("use local varaible"); }}); Thread.sleep(100); } System.out.println("pool execute over"); }Copy the code
      • Memory monitoring: Found that memory reached 150MB+ at one point

      • Modify the code again: follow remove after set

          public void run() {
                             localVariable.set(new LocalVariable());
         ​
                             System.out.println("use local varaible");
                             localVariable.remove();
                         }
        Copy the code
      • Memory monitoring: This is consistent with not introducing ThreadLocal

        This means that the introduction of ThreadLocal causes a memory leak of approximately 70MB+

    • Why does a memory leak occur?

      • Look at the ThreadLocalMap source code: Weak references are used

        static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; } } private static final int INITIAL_CAPACITY = 16; private Entry[] table; }Copy the code
      • When GC occurs, ThreadLocal must be reclaimed

      • Why does a memory leak occur: Data is unreachable but it’s real

        • Schematic diagram:

        • Process analysis:

          The top line

          1. An instance of ThreadLocal corresponds to a thread-specific static ThreadLocalMap, whose KEY is ThreadLocal, and whose value is what we set into it; ThreadLocal instances in the heap have a reference on the stack (strong)

          2. Once GC occurs, the ThreadLocal will be reclaimed, because the ThreadLocal is wrapped with a virtual reference. The reference of the ThreadLocal instance on the stack will be null, and the value inside the Entry will not be reclaimed. But the key in the map is ThreadLocal, which is null, so the value is not accessible

            The top line breaks

          The bottom line still exists

          1. The problem is that each thread has a ThreadLocalMap, which in turn has an Entry that points to the value inside

          2. However, the KEY is missing, which means that the value exists but cannot be referenced, causing a memory leak

            The current thread needs to be reclaimed as well

        • The details of using ThreadLocal: set followed by remove

    • Why, it looks like 2500MB is allocated (one thread allocates 5MB, executes 500 times, and has 5 threads), but internal memory is stable at 25MB during the memory check? Get /set will still clear, but not in time

      • Back to get source code:

        public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! GetEntry (this) threadLocalMap.entry e = map.getentry (this); if (e ! = null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; }}Copy the code
      • The getEntry (this) :

        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 getEntryAfterMiss return getEntryAfterMiss(key, I, e); }Copy the code
      • GetEntryAfterMiss:

        private Entry getEntryAfterMiss(ThreadLocal<? > key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e ! = null) { ThreadLocal<? > k = e.get(); if (k == key) return e; If (k == null) // If (k == null) // expungeStaleEntry (ThreadLocalMap); expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; }Copy the code
      • There’s also something related in the set method

      • There are also related things in remove:

    • Why does the JDK define weak references

      • If strong references are used, memory leaks are bound to occur

        • Defined as weak references, there is an opportunity to recycle, which is to handle the null key in ThreadLocalMap
        • If it is defined as a strong reference, there is no chance for this check
    • The big four references

      • Strong reference: something that is normally new

        • When a method is executed, the method is packaged into stack frames, object instances are allocated on the heap, references are placed on the stack, and references refer to object instances in the heap
        • As long as strong references exist, object instances in the heap are not recycled
      • SoftRefence: If a reference on the stack refers to an instance in the heap, it is recollected when it reaches OOM

      • WeakReference: collection is guaranteed whenever GC occurs

      • Virtual references: Can be reclaimed at any time

  • ThreadLocal is not thread-safe: when handling static variables

    • Multiple threads schedule static variables that are placed in a ThreadLocal and are thread unsafe

      • Code:

        package cn.enjoyedu.ch1.threadlocal; import cn.enjoyedu.tools.SleepTools; /** * */ public class ThreadLocalUnsafe implements Runnable {public static Number Number = new Number(0);  Public void run() {// Count for each thread with number.setnum (number.getnum ()+1); // Store it in ThreadLocal value.set(number); SleepTools.ms(2); System.out.println(thread.currentThread ().getName()+"="+value.get().getnum ()); } public static ThreadLocal<Number> value = new ThreadLocal<Number>() { }; public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(new ThreadLocalUnsafe()).start(); } } private static class Number { public Number(int num) { this.num = num; } private int num; public int getNum() { return num; } public void setNum(int num) { this.num = num; } @Override public String toString() { return "Number [num=" + num + "]"; }}}Copy the code
      • Run screenshot:

      • The reason:

        Although each thread keeps a copy of the variable, it operates independently in its own thread; But in this case, essentially, you’re saving object references that all refer to the same instance in the heap (because the object is statically modified)

      • Solution 1: Remove the static modifier from the object

        • Each thread keeps a reference to its own object, without raising security concerns
      • Solution 2:

        • Specifies an initial value for a generic in ThreadLocal at initialization time

          This is unsolved

  • Threads cooperate with each other: notification mode

    • Scene:

      • Thread A modifies the class attribute, and thread B continuously monitors the class attribute until the condition is met
    • Means of implementation:

      1. Polling: If thread B has a sleep function, it will cause unnecessary waste of resources

      2. Use the wait () + notify () :

        • Thread B calls the wait function, and when the notify() thread B– > triggers thread B

        • Wait, notify, and notifyAll are not Thread methods but Object methods.

        • Use: Be sure to have synchronized packages

          {/ sync/wait paradigm (objects) while (condition does not meet the) {object. Wait () / / calling wait () - > release object lock held by thread; When a wait thread is awakened, it will compete for the lock. Once it has the lock, it will check the condition, which is satisfied, and execute the business logic} // The condition satisfies the specific business logic code} // Notification paradigm sync(object){// Business logic, Change the condition object. Notify /notifyAll(); The notify/notifyAll is placed on the last line of the synchronized block because it does not release the lock.Copy the code
    • Wait and notify(

      • Two classes are defined: Express and TestWN

        • Express: There are two threads (waiting for a change in mileage, waiting for a change in location), and two types of wake up
        • TestWN:
      • Implementation details:

        • About the use of notify and notifyAll: Generally, a notifyAll is used

          • Notify: Only any thread that holds the lock will wake up.
          • NotifyAll: Wakes up all objects that hold the lock
        • About notify and notifyAll, wait

          • Wait and wake up on an object. If the waiting condition is a property of the class, then you have to pay attention to the details

            • You can use notify for one condition on an object

            • NotifyAll is used for multiple conditions on an object

              • Or you can just lock that condition, but that’s a little bit of a problem, you need to make sure that the object doesn’t change, so i++ won’t lock that
          • Must be in a synchronized code block
          • Exceptions need to be handled after a wait function: the JDK has an interrupt mechanism for built-in blocking methods
      • Code diagram:

        Express:

        package cn.enjoyedu.ch1.wn; Public class Express {public final static String CITY = "ShangHai"; private int km; /* private String site; Public Express() {} public Express(int km, String site) {this.km = km; this.site = site; } public synchronized void changeKm(){//TODO this.km = 101; notifyAll(); // Use notify. Wake up the thread at the wait point, wait kilometers are not fired because} /* change the location, */ public synchronized void changeSite(){this.site = "BeiJing"; notifyAll(); } public synchronized void waitKm(){//TODO while(this.km<100){try {wait(); System.out.println("Check Site thread[" +Thread.currentThread().getId() +"] is be notified"); } catch (InterruptedException e) {// All JDK implementation blocking methods have an interrupt handling mechanism. } } System.out.println("the Km is "+this.km+",I will change db"); Public synchronized void waitSite(){while(this.site.equals(CITY)){// Synchronized void waitSite(); This place uses while(when the condition is not met, the thread continues to sleep until the condition is met, triggering the notification) // Uses if, and it only does try {wait() once; System.out.println("Check Site thread["+Thread.currentThread().getId() +"] is be notified"); } catch (InterruptedException e) { e.printStackTrace(); Println ("the site is "+this.site+",I will call user"); }}Copy the code

        TestWN

        package cn.enjoyedu.ch1.wn; Wait /notify/notifyAll */ public class TestWN {private static Express Express = new Express(0, express.city); */ private static class CheckKm extends Thread{@override public void run() {express.waitkm ();  }} /* Check the thread of location change, do not meet the condition, */ private static class CheckSite extends Thread{@override public void run() {express.waitsite (); } } public static void main(String[] args) throws InterruptedException { for(int i=0; i<3; i++){ new CheckSite().start(); } for(int i=0; i<3; i++){ new CheckKm().start(); } Thread.sleep(1000); express.changeKm(); // notifyAll is a notifyAll; // notifyAll is a notifyAll;}Copy the code