Java ThreadLocal用法实例详解

本文实例讲述了Java ThreadLocal用法。分享给大家供大家参考,具体如下:

目录

  • ThreadLocal的基本使用
  • ThreadLocal实现原理
  • 源码分析(基于openjdk11)
    • get方法:

      • setInitialValue方法
      • getEntry方法
    • set方法
      • ThreadLocalMap的set方法

        • replaceStaleEntry方法
        • cleanSomeSlots方法
        • rehash方法
        • expungeStaleEntries方法
        • resize方法

ThreadLocal实现了Java中线程局部变量。所谓线程局部变量就是保存在每个线程中独有的一些数据,我们知道一个进程中的所有线程是共享该进程的资源的,线程对进程中的资源进行修改会反应到该进程中的其他线程上,如果我们希望一个线程对资源的修改不会影响到其他线程,那么就需要将该资源设为线程局部变量的形式。

ThreadLocal的基本使用

如下示例所示,定义两个ThreadLocal变量,然后分别在主线程和子线程中对线程局部变量进行修改,然后分别获取线程局部变量的值:

public class ThreadLocalTest {

  private static ThreadLocal<String> threadLocal1 = ThreadLocal.withInitial(() -> "threadLocal1 first value");
  private static ThreadLocal<String> threadLocal2 = ThreadLocal.withInitial(() -> "threadLocal2 first value");

  public static void main(String[] args) throws Exception{

    Thread thread = new Thread(() -> {

      System.out.println("================" + Thread.currentThread().getName() + " enter=================");

      // 子线程中打印出初始值
      printThreadLocalInfo();

      // 子线程中设置新值
      threadLocal1.set("new thread threadLocal1 value");
      threadLocal2.set("new thread threadLocal2 value");

      // 子线程打印出新值
      printThreadLocalInfo();

      System.out.println("================" + Thread.currentThread().getName() + " exit=================");
    });

    thread.start();
    // 等待新线程执行
    thread.join();

    // 在main线程打印threadLocal1和threadLocal2,验证子线程对这两个变量的修改是否会影响到main线程中的这两个值
    printThreadLocalInfo();
    // 在main线程中给threadLocal1和threadLocal2设置新值
    threadLocal1.set("main threadLocal1 value");
    threadLocal2.set("main threadLocal2 value");
    // 验证main线程中这两个变量是否为新值
    printThreadLocalInfo();
  }

  private static void printThreadLocalInfo() {
    System.out.println(Thread.currentThread().getName() + ": " + threadLocal1.get());
    System.out.println(Thread.currentThread().getName() + ": " + threadLocal2.get());
  }
}

运行结果如下:

================Thread-0 enter=================
Thread-0: threadLocal1 first value
Thread-0: threadLocal2 first value
Thread-0: new thread threadLocal1 value
Thread-0: new thread threadLocal2 value
================Thread-0 exit=================
main: threadLocal1 first value
main: threadLocal2 first value
main: main threadLocal1 value
main: main threadLocal2 value

如果子线程对threadLocal1threadLocal2的修改会影响到main线程中的threadLocal1threadLocal2,那么在main线程第一次printThreadLocalInfo();打印出的应该是修改后的新值,即为new thread threadLocal1 valuenew thread threadLocal2 value和,但实际打印结果并不是这样,说明在新线程中对threadLocal1threadLocal2的修改并不会影响到main线程中的这两个变量,似乎main线程中的threadLocal1threadLocal2作用域仅局限于main线程,新线程中的threadLocal1threadLocal2作用域仅局限于新线程,这就是线程局部变量的由来。

ThreadLocal实现原理

如下图所示

每个线程对象里会持有一个java.lang.ThreadLocal.ThreadLocalMap类型的threadLocals成员变量,而ThreadLocalMap里有一个java.lang.ThreadLocal.ThreadLocalMap.Entry[]类型的table成员,这是一个数组,数组元素是Entry类型,Entry中相当于有一个keyvaluekey指向所有线程共享的java.lang.ThreadLocal对象,value指向各线程私有的变量,这样保证了线程局部变量的隔离性,每个线程只是读取和修改自己所持有的那个value对象,相互之间没有影响。

源码分析(基于openjdk11)

源码包括ThreadLocalThreadLocalMapThreadLocalMapThreadLocal内定义的一个静态内部类,用于存储实际的数据。当调用ThreadLocalget或者set方法时都有可能创建当前线程的threadLocals成员(ThreadLocalMap类型)。

get方法:

ThreadLocal的get方法定义如下

  /**
   * Returns the value in the current thread's copy of this
   * thread-local variable. If the variable has no value for the
   * current thread, it is first initialized to the value returned
   * by an invocation of the {@link #initialValue} method.
   *
   * @return the current thread's value of this thread-local
   */
  public T get() {
  	// 获取当前线程
    Thread t = Thread.currentThread();
    // 获取当前线程的threadLocals成员变量,这是一个ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    // threadLocals不为null则直接从threadLocals中取出ThreadLocal
    // 对象对应的值
    if (map != null) {
    	// 从map中获取当前ThreadLocal对象对应Entry对象
      ThreadLocalMap.Entry e = map.getEntry(this);
      if (e != null) {
      	// 获取ThreadLocal对象对应的value值
        @SuppressWarnings("unchecked")
        T result = (T)e.value;
        return result;
      }
    }
    // threadLocals为null,则需要创建ThreadLocalMap对象并赋给
    // threadLocals,将当前ThreadLocal对象作为key,调用initialValue
    // 获得的初始值作为value,放置到threadLocals的entry中;
    // 或者threadLocals不为null,但在threadLocals中未
    // 找到当前ThreadLocal对象对应的entry,则需要向threadLocals添加新的
    // entry,该entry以当前的ThreadLocal对象作为key,调用initialValue
    // 获得的值作为value
    return setInitialValue();
  }
  /**
   * Get the map associated with a ThreadLocal. Overridden in
   * InheritableThreadLocal.
   *
   * @param t the current thread
   * @return the map
   */
  ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
  }

ThreadthreadLocals为null,或者在ThreadthreadLocals中未找到当前ThreadLocal对象对应的entry,则进入到setInitialValue方法;否则进入到ThreadLocalMapgetEntry方法。

setInitialValue方法

定义如下:

  private T setInitialValue() {
  	// 获取初始值,如果我们在定义ThreadLocal对象时实现了ThreadLocal
  	// 的initialValue方法,就会调用我们自定义的方法来获取初始值,否则
  	// 使用initialValue的默认实现返回null值
    T value = initialValue();
    Thread t = Thread.currentThread();
    // 获取当前线程的threadLocals成员
    ThreadLocalMap map = getMap(t);
    if (map != null) {
    	// 若threadLocals存在则将ThreadLocal对象对应的value设置为初始值
      map.set(this, value);
    } else {
    	// 否则创建threadLocals对象并设置初始值
      createMap(t, value);
    }
    if (this instanceof TerminatingThreadLocal) {
      TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
    }
    return value;
  }

createMap方法实现

  /**
   * Create the map associated with a ThreadLocal. Overridden in
   * InheritableThreadLocal.
   *
   * @param t the current thread
   * @param firstValue value for the initial entry of the map
   */
  void createMap(Thread t, T firstValue) {
  	// 创建一个ThreadLocalMap对象,用当前ThreadLocal对象和初始值value来
  	// 构造ThreadLocalMap中table的第一个entry。ThreadLocalMap对象赋
  	// 给线程的threadLocals成员
    t.threadLocals = new ThreadLocalMap(this, firstValue);
  }

ThreadLocalMap的构造方法定义如下:

    /**
     * Construct a new map initially containing (firstKey, firstValue).
     * ThreadLocalMaps are constructed lazily, so we only create
     * one when we have at least one entry to put in it.
     */
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    	// 构造table数组,数组大小为INITIAL_CAPACITY
      table = new Entry[INITIAL_CAPACITY];
      // 计算key(ThreadLocal对象)在table中的索引
      int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
      // 用ThreadLocal对象和value来构造entry对象,并放到table的第i个位置
      table[i] = new Entry(firstKey, firstValue);
      size = 1;
      // 设置table的阈值,当table中元素个数超过该阈值时需要对table
      // 进行resize,通常在调用ThreadLocalMap的set方法时会发生resize
      setThreshold(INITIAL_CAPACITY);
    }
    /**
     * Set the resize threshold to maintain at worst a 2/3 load factor.
     */
    private void setThreshold(int len) {
      threshold = len * 2 / 3;
    }

这里firstKey.threadLocalHashCode是ThreadLocal中定义的一个hashcode,使用该hashcode进行hash运算从而找到该ThreadLocal对象对应的entry在table中的索引。

getEntry方法

定义如下:

    /**
     * Get the entry associated with key. This method
     * itself handles only the fast path: a direct hit of existing
     * key. It otherwise relays to getEntryAfterMiss. This is
     * designed to maximize performance for direct hits, in part
     * by making this method readily inlinable.
     *
     * @param key the thread local object
     * @return the entry associated with key, or null if no such
     */
    private Entry getEntry(ThreadLocal<?> key) {
    	// 根据ThreadLocal的hashcode计算该ThreadLocal对象在table中的位置
      int i = key.threadLocalHashCode & (table.length - 1);
      Entry e = table[i];
      // e为null则table不存在key对应的entry;
      // e.get() != key 可能是由于hash冲突导致key对应的entry在table
      // 的另外一个位置,需要继续查找
      if (e != null && e.get() == key)
        return e;
      else
      	// e==null或者e.get() != key 继续查找key对应的entry
        return getEntryAfterMiss(key, i, e);
    }

getEntryAfterMiss方法定义如下:

    /**
     * Version of getEntry method for use when key is not found in
     * its direct hash slot.
     *
     * @param key the thread local object
     * @param i the table index for key's hash code
     * @param e the entry at table[i]
     * @return the entry associated with key, or null if no such
     */
    private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e){
      Entry[] tab = table;
      int len = tab.length;

			// 从table的第i个位置一直往后找,直到找到键为key的entry为止
      while (e != null) {
        ThreadLocal<?> k = e.get();
        // 若k==key,则找到了entry
        if (k == key)
          return e;
        // k == null 需要删除该entry
        if (k == null)
          expungeStaleEntry(i);
        // k != key && k != null 继续往后寻找,nextIndex就是取(i+1)
        // 即table中第(i+1)个位置的entry
        else
          i = nextIndex(i, len);
        e = tab[i];
      }
      return null;
    }

expungeStaleEntry方法删除key为null的entry,删除后对staleSlot位置的entry和其后第一个为null的entry之间的entry进行一个rehash操作,rehash的目的是降低table发生碰撞的概率:

    /**
     * Expunge a stale entry by rehashing any possibly colliding entries
     * lying between staleSlot and the next null slot. This also expunges
     * any other stale entries encountered before the trailing null. See
     * Knuth, Section 6.4
     *
     * @param staleSlot index of slot known to have null key
     * @return the index of the next null slot after staleSlot
     * (all between staleSlot and this slot will have been checked
     * for expunging).
     */
    private int expungeStaleEntry(int staleSlot) {
      Entry[] tab = table;
      int len = tab.length;

      // expunge entry at staleSlot
      // 删除staleSlot位置的entry
      tab[staleSlot].value = null;
      tab[staleSlot] = null;
      // table中元素个数减一
      size--;

      // Rehash until we encounter null
      // 将table中staleSlot处entry和下一个为null的entry之间的
      // entry重新进行hash放置到新的位置
      // 遇到的entry的key为null则删除该entry
      Entry e;
      int i;
      for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        // e是下一个entry
        ThreadLocal<?> k = e.get();
        if (k == null) {
        	// 若entry的key为null,则删除
          e.value = null;
          tab[i] = null;
          size--;
        } else {
        	// entry的key不为null,需要将entry放到新的位置
          int h = k.threadLocalHashCode & (len - 1);
          if (h != i) {
            tab[i] = null;

            // Unlike Knuth 6.4 Algorithm R, we must scan until
            // null because multiple entries could have been stale.
            // tab[h]不为null则发生冲突,继续寻找下一个位置
            while (tab[h] != null)
              h = nextIndex(h, len);
            tab[h] = e;
          }
        }
      }
      return i;
    }

set方法

ThreadLocal的set方法定义如下:

  /**
   * Sets the current thread's copy of this thread-local variable
   * to the specified value. Most subclasses will have no need to
   * override this method, relying solely on the {@link #initialValue}
   * method to set the values of thread-locals.
   *
   * @param value the value to be stored in the current thread's copy of
   *    this thread-local.
   */
  public void set(T value) {
    Thread t = Thread.currentThread();
    // 获取当前线程的threadLocals
    ThreadLocalMap map = getMap(t);
    // threadLocals不为null直接设置新值
    if (map != null) {
      map.set(this, value);
    } else {
    	// threadLocals为null则需要创建ThreadLocalMap对象并赋给
    	// Thread的threadLocals成员
      createMap(t, value);
    }
  }

createMap前面已经分析过,接下来分析ThreadLocalMap的set方法

ThreadLocalMap的set方法

ThreadLocalMap的set方法定义如下,将当前的ThreadLocal对象作为key,传入的value为值,用key和value创建entry,放到table中适当的位置:

    /**
     * Set the value associated with key.
     *
     * @param key the thread local object
     * @param value the value to be set
     */
    private void set(ThreadLocal<?> key, Object value) {

      // We don't use a fast path as with get() because it is at
      // least as common to use set() to create new entries as
      // it is to replace existing ones, in which case, a fast
      // path would fail more often than not.

      Entry[] tab = table;
      int len = tab.length;
      // 用key计算entry在table中的位置
      int i = key.threadLocalHashCode & (len-1);

			// tab[i]不为null的话,则第i个位置已经存在有效的entry,需要继续
			// 往后寻找新的位置
      for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();

				// 找到与key相同的entry,直接更新value的值
        if (k == key) {
          e.value = value;
          return;
        }

				// 遇到key为null的entry,删除该entry
        if (k == null) {
          replaceStaleEntry(key, value, i);
          return;
        }
      }

			// 此时第i个位置entry为null,将新entry放置到这个位置
      tab[i] = new Entry(key, value);
      int sz = ++size;
      // 试图清除无效的entry,若清除失败并且table中有效entry个数
      // 大于threshold,这进行rehash操作
      if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
    }

replaceStaleEntry方法

replaceStaleEntry的作用是用set方法传过来的key和value构造entry,将这个entry放到staleSlot后面的某个位置:

    /**
     * Replace a stale entry encountered during a set operation
     * with an entry for the specified key. The value passed in
     * the value parameter is stored in the entry, whether or not
     * an entry already exists for the specified key.
     *
     * As a side effect, this method expunges all stale entries in the
     * "run" containing the stale entry. (A run is a sequence of entries
     * between two null slots.)
     *
     * @param key the key
     * @param value the value to be associated with key
     * @param staleSlot index of the first stale entry encountered while
     *     searching for key.
     */
    private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                    int staleSlot) {
      Entry[] tab = table;
      int len = tab.length;
      Entry e;

      // Back up to check for prior stale entry in current run.
      // We clean out whole runs at a time to avoid continual
      // incremental rehashing due to garbage collector freeing
      // up refs in bunches (i.e., whenever the collector runs).
      // 从staleSlot往前找到第一个key为null的entry的位置
      int slotToExpunge = staleSlot;
      for (int i = prevIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = prevIndex(i, len))
        if (e.get() == null)
          slotToExpunge = i;

      // Find either the key or trailing null slot of run, whichever
      // occurs first
      // 从staleSlot位置往后寻找
      for (int i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();

        // If we find key, then we need to swap it
        // with the stale entry to maintain hash table order.
        // The newly stale slot, or any other stale slot
        // encountered above it, can then be sent to expungeStaleEntry
        // to remove or rehash all of the other entries in run.
        // 若k与key相同,则直接更新value
        if (k == key) {
          e.value = value;
					// 将原来staleSlot位置的entry放置到第i个位置,此时tab[i]处的entry的key为null
          tab[i] = tab[staleSlot];
          tab[staleSlot] = e;

          // Start expunge at preceding stale entry if it exists
          // 从staleSlot处往前未找到key为null的entry
          if (slotToExpunge == staleSlot)
          	// tab[i]处entry的key为null,也即tab[slotToExpunge]处entry的key为null
            slotToExpunge = i;
          // 清除slotToExpunge位置的entry并进行rehash操作.....
          cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
          return;
        }

        // If we didn't find stale entry on backward scan, the
        // first stale entry seen while scanning for key is the
        // first still present in the run.
        if (k == null && slotToExpunge == staleSlot)
          slotToExpunge = i;
      }

      // If key not found, put new entry in stale slot
      tab[staleSlot].value = null;
      tab[staleSlot] = new Entry(key, value);

      // If there are any other stale entries in run, expunge them
      if (slotToExpunge != staleSlot)
        cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
    }

以下源码只可意会,不可言传…不再做说明

cleanSomeSlots方法

cleanSomeSlots方法:

    /**
     * Heuristically scan some cells looking for stale entries.
     * This is invoked when either a new element is added, or
     * another stale one has been expunged. It performs a
     * logarithmic number of scans, as a balance between no
     * scanning (fast but retains garbage) and a number of scans
     * proportional to number of elements, that would find all
     * garbage but would cause some insertions to take O(n) time.
     *
     * @param i a position known NOT to hold a stale entry. The
     * scan starts at the element after i.
     *
     * @param n scan control: {@code log2(n)} cells are scanned,
     * unless a stale entry is found, in which case
     * {@code log2(table.length)-1} additional cells are scanned.
     * When called from insertions, this parameter is the number
     * of elements, but when from replaceStaleEntry, it is the
     * table length. (Note: all this could be changed to be either
     * more or less aggressive by weighting n instead of just
     * using straight log n. But this version is simple, fast, and
     * seems to work well.)
     *
     * @return true if any stale entries have been removed.
     */
    private boolean cleanSomeSlots(int i, int n) {
      boolean removed = false;
      Entry[] tab = table;
      int len = tab.length;
      do {
        i = nextIndex(i, len);
        Entry e = tab[i];
        if (e != null && e.get() == null) {
          n = len;
          removed = true;
          i = expungeStaleEntry(i);
        }
      } while ( (n >>>= 1) != 0);
      return removed;
    }

rehash方法

rehash方法:

    /**
     * Re-pack and/or re-size the table. First scan the entire
     * table removing stale entries. If this doesn't sufficiently
     * shrink the size of the table, double the table size.
     */
    private void rehash() {
      expungeStaleEntries();

      // Use lower threshold for doubling to avoid hysteresis
      if (size >= threshold - threshold / 4)
        resize();
    }

expungeStaleEntries方法

expungeStaleEntries方法:

    /**
     * Expunge all stale entries in the table.
     */
    private void expungeStaleEntries() {
      Entry[] tab = table;
      int len = tab.length;
      for (int j = 0; j < len; j++) {
        Entry e = tab[j];
        if (e != null && e.get() == null)
          expungeStaleEntry(j);
      }
    }

resize方法

resize方法:

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

      for (Entry e : oldTab) {
        if (e != null) {
          ThreadLocal<?> k = e.get();
          if (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;
    }

更多java相关内容感兴趣的读者可查看本站专题:《Java进程与线程操作技巧总结》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》

希望本文所述对大家java程序设计有所帮助。

时间: 2019-09-19

Java源码解析ThreadLocal及使用场景

ThreadLocal是在多线程环境下经常使用的一个类. 这个类并不是为了解决多线程间共享变量的问题.举个例子,在一个电商系统中,用一个Long型变量表示某个商品的库存量,多个线程需要访问库存量进行销售,并减去销售数量,以更新库存量.在这个场景中,是不能使用ThreadLocal类的. ThreadLocal适用的场景是,多个线程都需要使用一个变量,但这个变量的值不需要在各个线程间共享,各个线程都只使用自己的这个变量的值.这样的场景下,可以使用ThreadLocal.此外,我们使用ThreadL

java ThreadLocal使用案例详解

本文借由并发环境下使用线程不安全的SimpleDateFormat优化案例,帮助大家理解ThreadLocal. 最近整理公司项目,发现不少写的比较糟糕的地方,比如下面这个: public class DateUtil { private final static SimpleDateFormat sdfyhm = new SimpleDateFormat( "yyyyMMdd"); public synchronized static Date parseymdhms(String

java 中ThreadLocal 的正确用法

java 中ThreadLocal 的正确用法 用法一:在关联数据类中创建private static ThreadLocalThreaLocal的JDK文档中说明:ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread.如果我们希望通过某个类将状态(例如用户ID.事务ID)与线程关联起来,那么通常在这个类中定义private s

深入解析Java中ThreadLocal线程类的作用和用法

ThreadLocal与线程成员变量还有区别,ThreadLocal该类提供了线程局部变量.这个局部变量与一般的成员变量不一样,ThreadLocal的变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本,这是Java API中的描述,通过阅读API源码,发现并非副本,副本什么概念?克隆品? 或者是别的样子,太模糊.   准确的说,应该是ThreadLocal类型的变量内部的注册表(Map<Thread,T>)发生了变化,但ThreadLocal类型的变量本身的确是一个,这才是本质!  

Java多线程编程之ThreadLocal线程范围内的共享变量

模拟ThreadLocal类实现:线程范围内的共享变量,每个线程只能访问他自己的,不能访问别的线程. package com.ljq.test.thread; import java.util.HashMap; import java.util.Map; import java.util.Random; /** * 线程范围内的共享变量 * * 三个模块共享数据,主线程模块和AB模块 * * @author Administrator * */ public class ThreadScopeS

Java 中ThreadLocal类详解

ThreadLocal类,代表一个线程局部变量,通过把数据放在ThreadLocal中,可以让每个线程创建一个该变量的副本.也可以看成是线程同步的另一种方式吧,通过为每个线程创建一个变量的线程本地副本,从而避免并发线程同时读写同一个变量资源时的冲突. 示例如下: import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import ja

简单分析Java线程编程中ThreadLocal类的使用

一.概述   ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许把它命名为ThreadLocalVar更加合适.线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突.   从线程的角度看,每个线程都保持一个对

Java多线程编程中ThreadLocal类的用法及深入

ThreadLocal,直译为"线程本地"或"本地线程",如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,我认为应该叫做 ThreadLocalVariable(线程局部变量)才对,真不理解为什么当初 Sun 公司的工程师这样命名. 早在 JDK 1.2 的时代,java.lang.ThreadLocal 就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至今没有得到广泛使用.其实它还是挺有用的,不相信的话,我们一起

Java 并发编程之ThreadLocal详解及实例

Java 理解 ThreadLocal 摘要: ThreadLocal 又名线程局部变量,是 Java 中一种较为特殊的线程绑定机制,用于保证变量在不同线程间的隔离性,以方便每个线程处理自己的状态.进一步地,本文以ThreadLocal类的源码为切入点,深入分析了ThreadLocal类的作用原理,并给出应用场景和一般使用步骤. 一. 对 ThreadLocal 的理解 1). ThreadLocal 概述 ThreadLocal 又名 线程局部变量,是 Java 中一种较为特殊的 线程绑定机制

快速了解Java中ThreadLocal类

最近看Android FrameWork层代码,看到了ThreadLocal这个类,有点儿陌生,就翻了各种相关博客一一拜读:自己随后又研究了一遍源码,发现自己的理解较之前阅读的博文有不同之处,所以决定自己写篇文章说说自己的理解,希望可以起到以下作用: - 可以疏通研究结果,加深自己的理解: - 可以起到抛砖引玉的作用,帮助感兴趣的同学疏通思路: - 分享学习经历,同大家一起交流和学习. 一. ThreadLocal 是什么 ThreadLocal 是Java类库的基础类,在包java.lang下

java 中ThreadLocal本地线程和同步机制的比较

ThreadLocal的设计 首先看看ThreadLocal的接口: Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值 void set(Object value); // 设置当前线程的线程局部变量副本的值 ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的.该方法返回当

JAVA中实现原生的 socket 通信机制原理

本文介绍了JAVA中实现原生的 socket 通信机制原理,分享给大家,具体如下: 当前环境 jdk == 1.8 知识点 socket 的连接处理 IO 输入.输出流的处理 请求数据格式处理 请求模型优化 场景 今天,和大家聊一下 JAVA 中的 socket 通信问题.这里采用最简单的一请求一响应模型为例,假设我们现在需要向 baidu 站点进行通信.我们用 JAVA 原生的 socket 该如何实现. 建立 socket 连接 首先,我们需要建立 socket 连接(核心代码) impor

Java中四种线程池的使用示例详解

在什么情况下使用线程池? 1.单个任务处理的时间比较短 2.将需处理的任务的数量大 使用线程池的好处: 1.减少在创建和销毁线程上所花的时间以及系统资源的开销 2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及"过度切换". 本文详细的给大家介绍了关于Java中四种线程池的使用,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: FixedThreadPool 由Executors的newFixedThreadPool方法创建.它是一种线程数量固定的线程

java 中ThreadLocal实例分析

java  中ThreadLocal实例分析 从概念上理解,threadlocal使变量在多个线程中相互隔离实现线程安全,threadlocal包装的变量最终都专属于对应的每个线程,线程之间相互独立,用一个具体实现来说明: public interface Consumer { int consume(); } public class ComsumeThread implements Runnable { private Consumer consumer; public ComsumeThr

详解java中保持compareTo和equals同步

详解java中保持compareTo和equals同步 摘要 : 介绍重写equlas()和comparable接口,两者进行不相同的判断.从而使两者的对应的list.indexOf()与 Collections.binarySearch()得到的不一样. 在Java中我们常使用Comparable接口来实现排序,其中compareTo是实现该接口方法.我们知道compareTo返回0表示两个对象相等,返回正数表示大于,返回负数表示小于.同时我们也知道equals也可以判断两个对象是否相等,那么

java中进程与线程_三种实现方式总结(必看篇)

一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是一个线程. 进程:进程是指处于运行过程中的程序,并且具有一定的独立功能.进程是系统进行资源分配和调度的一个单位.当程序进入内存运行时,即为进程. 进程的三个特点: 1:独立性:进程是系统中独立存在的实体,它可以独立拥有资源,每一个进程都有自己独立的地址空间,没有进程本身的运行,用户进程不可以直接访问其他进程的地址空间. 2:

Java中LocalCache本地缓存实现代码

前言 本次分享探讨java平台的本地缓存,是指占用JVM的heap区域来缓冲存储数据的缓存组件. 一.本地缓存应用场景 localcache有着极大的性能优势: 1. 单机情况下适当使用localcache会使应用的性能得到很大的提升. 2. 集群环境下对于敏感性要求不高的数据可以使用localcache,只配置简单的失效机制来保证数据的相对一致性. 哪些数据可以存储到本地缓存? 1.访问频繁的数据: 2.静态基础数据(长时间内不变的数据): 3.相对静态数据(短时间内不变的数据). 二.jav