欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 詳解ThreadLocal及InheritableThreadLocal

 更新時(shí)間:2017年01月18日 14:30:32   投稿:lqh  
這篇文章主要介紹了Android 詳解ThreadLocal及InheritableThreadLocal的相關(guān)資料,需要的朋友可以參考下

 Android  詳解ThreadLocal及InheritableThreadLocal

概要:

因?yàn)樵赼ndroid中經(jīng)常用到handler來處理異步任務(wù),通常用于接收消息,來操作UIThread,其中提到涉及到的looper對(duì)象就是保存在Threadlocal中的,因此研究下Threadlocal的源碼。

  分析都是基于android sdk 23 源碼進(jìn)行的,ThreadLocal在android和jdk中的實(shí)現(xiàn)可能并不一致。

  在最初使用Threadlocal的時(shí)候,很容易會(huì)產(chǎn)生的誤解就是threadlocal就是一個(gè)線程。

  首先來看下Threadlocal的簡單例子:

  一個(gè)簡單的Person類:

public class Person {

  public String name;
  public int age;
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

   一個(gè)簡單的activity:

 public class MainActivity extends Activity {

  //ThreadLocal初始化
  private ThreadLocal<Person> mThreadLocal = new ThreadLocal<Person>();
  private Person mPerson = new Person("王大俠", 100);

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //將mPerson對(duì)象設(shè)置進(jìn)去
    mThreadLocal.set(mPerson);
    Log.d("主線程", " 名字:" + mThreadLocal.get().name + " 年齡:" + mThreadLocal.get().age);
    }
  }

  運(yùn)行看看是否能得到mperson對(duì)象:

04-19 13:14:31.053 2801-2801/com.example.franky.myapplication d/主線程:   名字:王大俠  年齡:100

  果然得到了!說明當(dāng)前線程確實(shí)已經(jīng)存儲(chǔ)了mPerson對(duì)象的引用。

  那么我們開啟個(gè)子線程看看適合還能獲取到mPerson對(duì)象呢:

public class MainActivity extends Activity {

//ThreadLocal初始化
private ThreadLocal<Person> mThreadLocal = new ThreadLocal<Person>();
private Person mPerson = new Person("王大俠", 100);

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //將mPerson對(duì)象設(shè)置進(jìn)去
  mThreadLocal.set(mPerson);
  new Thread(new Runnable() {
    @Override
    public void run() {
      Log.d("子線程", " 名字:" + mThreadLocal.get().name + " 年齡:" + mThreadLocal.get().age);
    }
  }).start();
}
}

運(yùn)行看看結(jié)果:

`java.lang.NullPointerException: Attempt to read from field '
java.lang.String com.example.franky.myapplication.Person.name' on a null object reference`

  哈哈,報(bào)錯(cuò)信息很明顯,空指針異常,這清楚的表明子線程是獲取不到mperson對(duì)象的,但可能到這里一些朋友可能有些暈了,明明我通過set()方法將mperson設(shè)置給threadlocal對(duì)象了啊,為啥在這里get()不到呢?

   這里我們開始追蹤threadlocal的源碼看看有沒有線索來解釋這個(gè)疑問。

首先我們可以看看threadlocal的構(gòu)造方法:

/**
 * Creates a new thread-local variable.
 */
public ThreadLocal() {}

  構(gòu)造方法平淡無奇,那么我們瞅瞅threadlocal的類說明吧,看看有沒有發(fā)現(xiàn):

implements a thread-local storage, that is,
 a variable for which each thread * has its own value.
 all threads share the same {@code threadlocal} object, 
* but each sees a different value when accessing it, and 
changes made by one * thread do not affect the other threads.
 the implementation supports * {@code null} values.

個(gè)人英文其實(shí)不是很好,大致的意思是每個(gè)線程都能在自己的線程保持一個(gè)對(duì)象,如果在一個(gè)線程改變對(duì)象的屬性不會(huì)影響其他線程。但我們不要誤讀,如果某個(gè)對(duì)象是共享變量,那么在某個(gè)線程中改變它時(shí),其他線程訪問的時(shí)候其實(shí)該對(duì)象也被改變了,所以并不是說ThreadLocal是線程安全的,我們只要理解ThreadLocal是能在當(dāng)前線程保存一個(gè)對(duì)象的,這樣我們不用到處傳遞這個(gè)對(duì)象。

那ThreadLocal是線程嗎?其實(shí)看看threadlocal有沒有繼承thread類就知道了:

public class ThreadLocal<T> {
}

答案是沒有~~,這說明threadlocal并不是線程。

明白了這點(diǎn),那我們繼續(xù)往下看看ThreadLocal是如何將對(duì)象保存起來的,瞅瞅set()方法:

public void set(T value) {
  Thread currentThread = Thread.currentThread();
  Values values = values(currentThread);
  if (values == null) {
    values = initializeValues(currentThread);
  }
  values.put(this, value);
}

首先通過Thread currentthread = thread.currentthread();獲取到當(dāng)前線程

然后currentthread作為方法參數(shù)傳遞給了vlaues方法:

 Values values(Thread current) {
  return current.localValues;
}

這里我們看到return的是thread類的一個(gè)成員變量,我們瞅瞅Thread類中的這個(gè)變量:

ThreadLocal.Values localValues;

這里我們看到localvalues成員變量的類型就是ThreadLocal.Values

這個(gè)類其實(shí)是ThreadLocal的內(nèi)部類。

然后這里判斷得到的values對(duì)象是不是null,也就是說Thread類中的成員變量localvalues是不是null,由于我們是初次設(shè)置,所以這個(gè)對(duì)象肯定是null,那繼續(xù)走

values initializevalues(thread current) {  return current.localvalues = new values();}

很明顯直接給localvalues變量new了一個(gè)value對(duì)象。那我們看看values對(duì)象里有啥:

首先看看構(gòu)造:

 Values() {
    initializeTable(INITIAL_SIZE);
    this.size = 0;
    this.tombstones = 0;
  }

看起來是初始化了一些成員變量的值,INITIAL_SIZE的值為16,

看看initializeTable(INITIAL_SIZE)這個(gè)方法是做啥的:

 private void initializeTable(int capacity) {
    this.table = new Object[capacity * 2];
    this.mask = table.length - 1;
    this.clean = 0;
    this.maximumLoad = capacity * 2 / 3; // 2/3
  }

初始化了長度為32的table數(shù)組,mask為31,clean為0,maximumLoad為10。

又是一堆成員變量,那只好看看變量的說明是做啥的:

這個(gè)table很簡單就是個(gè)object[]類型,意味著可以存放任何對(duì)象,變量說明:

  /**
   * Map entries. Contains alternating keys (ThreadLocal) and values.
   * The length is always a power of 2.
   */
  private Object[] table;

啊!原來這里就是存放保存的對(duì)象的。

其他的變量再看看:

/** Used to turn hashes into indices. */
  private int mask;

 /** Number of live entries. */
  private int size;

 /** Number of tombstones. */
  private int tombstones;

 /** Maximum number of live entries and tombstones. */
  private int maximumLoad;
 /** Points to the next cell to clean up. */
  private int clean;

這樣看來mask是用來計(jì)算數(shù)組下標(biāo)的,size其實(shí)是存活的保存的對(duì)象數(shù)量,tombstones是過時(shí)的對(duì)象數(shù)量,maximumLoad是最大的保存數(shù)量,clean是指向的下一個(gè)要清理的位置。大概明白了這些我們?cè)倮^續(xù)看:

values.put(this, value);

繼續(xù)追蹤:

/**
   * Sets entry for given ThreadLocal to given value, creating an
   * entry if necessary.
   */
  void put(ThreadLocal<?> key, Object value) {
    cleanUp();

    // Keep track of first tombstone. That's where we want to go back
    // and add an entry if necessary.
    int firstTombstone = -1;

    for (int index = key.hash & mask;; index = next(index)) {
      Object k = table[index];

      if (k == key.reference) {
        // Replace existing entry.
        table[index + 1] = value;
        return;
      }

      if (k == null) {
        if (firstTombstone == -1) {
          // Fill in null slot.
          table[index] = key.reference;
          table[index + 1] = value;
          size++;
          return;
        }

        // Go back and replace first tombstone.
        table[firstTombstone] = key.reference;
        table[firstTombstone + 1] = value;
        tombstones--;
        size++;
        return;
      }

      // Remember first tombstone.
      if (firstTombstone == -1 && k == TOMBSTONE) {
        firstTombstone = index;
      }
    }
  }

該方法直接將this對(duì)象和要保存的對(duì)象傳遞了進(jìn)來,

第一行的cleanUp()其實(shí)是用來對(duì)table執(zhí)行清理的,比如清理一些過時(shí)的對(duì)象,檢查是否對(duì)象的數(shù)量是否超過設(shè)置值,或者擴(kuò)容等,這里不再細(xì)說,有興趣大家可以研究下。

然后利用key.hash&mask計(jì)算下標(biāo),這里key.hash的初始化值:

private static AtomicInteger hashCounter = new AtomicInteger(0);
private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);

然后注釋說為了確保計(jì)算的下標(biāo)指向的是key值而不是value,當(dāng)然為啥用上述數(shù)值進(jìn)行計(jì)算就能保證獲取的key值,貌似是和這個(gè)0x61c88647數(shù)值有關(guān),再深入的大家可以留言。

然后最重要的就是

if (firstTombstone == -1) {
        // Fill in null slot.
        table[index] = key.reference;
        table[index + 1] = value;
        size++;
        return;
      }

這里將自身的引用,而且是弱引用,放在了table[index]上,將value放在它的下一個(gè)位置,保證key和value是排列在一起的,這樣其實(shí)我們知道了key其實(shí)是threadlocal的引用,值是value,它們一同被放置在table數(shù)組內(nèi)。

所以現(xiàn)在的情況是這樣,Thread類中的成員變量localValues是ThreadLocal.Values類型,所以說白了就是當(dāng)前線程持有了ThreadLocal.Values這樣的數(shù)據(jù)結(jié)構(gòu),我們?cè)O(shè)置的value全部都存儲(chǔ)在里面了,當(dāng)然如果我們?cè)谝粋€(gè)線程中new了很多ThreadLocal對(duì)象,其實(shí)指向都是Thread類中的成員變量localValues,而且如果new了很多ThreadLocal對(duì)象,其實(shí)都是放在table中的不同位置的。

那接下來看看get()方法:

 public T get() {
  // Optimized for the fast path.
  Thread currentThread = Thread.currentThread();
  Values values = values(currentThread);
  if (values != null) {
    Object[] table = values.table;
    int index = hash & values.mask;
    if (this.reference == table[index]) {
      return (T) table[index + 1];
    }
  } else {
    values = initializeValues(currentThread);
  }

  return (T) values.getAfterMiss(this);
}

代碼比較簡單了,首先還是獲取當(dāng)前線程,然后獲取當(dāng)前線程的Values對(duì)象,也就是Thread類中的成員變量localValues,然后拿到Values對(duì)象的table數(shù)組,計(jì)算下標(biāo),獲取保存的對(duì)象,當(dāng)然如果沒有獲取到return (T) values.getAfterMiss(this),就是返回null了,其實(shí)看方法Object getAfterMiss(ThreadLocal<?> key)中的這個(gè)代碼:

Object value = key.initialValue();

  protected T initialValue() {
  return null;
}

就很清楚了,當(dāng)然我們可以復(fù)寫這個(gè)方法來實(shí)現(xiàn)自定義返回,大家有興趣可以試試。

到此我們?cè)倩剡^頭來看看開始的疑問,為啥mThreadLocal在子線程獲取不到mPerson對(duì)象呢?原因就在于子線程獲取自身線程中的localValues變量中并未保存mPerson,真正保存的是主線程,所以我們是獲取不到的。

看完了ThreadLocal我們?cè)倏纯此囊粋€(gè)子類InheritableThreadLocal,該類和ThreadLocal最大的不同就是它可以在子線程獲取到保存的對(duì)象,而ThreadLocal只能在同一個(gè)線程,我們看看簡單的例子:

public class MainActivity extends Activity {

private InheritableThreadLocal<Person> mInheritableThreadLocal = new InheritableThreadLocal<Person>();
private Person mPerson = new Person("王大俠", 100);

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //將mPerson設(shè)置到當(dāng)前線程
  mInheritableThreadLocal.set(mPerson);
  Log.d("主線程"+Thread.currentThread().getName(), " 名字:" + mInheritableThreadLocal.get().name + " 年齡:" + mInheritableThreadLocal.get().age);
  new Thread(new Runnable() {
    @Override
    public void run() {
      Log.d("子線程"+Thread.currentThread().getName(), " 名字:" + mInheritableThreadLocal.get().name + " 年齡:" + mInheritableThreadLocal.get().age);
    }
  }).start();
}}

運(yùn)行看看輸出:

04-21 13:09:11.046 19457-19457/com.example.franky.myapplication D/主線程main:  名字:王大俠 年齡:100
04-21 13:09:11.083 19457-21729/com.example.franky.myapplication D/子線程Thread-184:  名字:王大俠 年齡:100

很明顯在子線程也獲取到了mPerson對(duì)象,那它是如何實(shí)現(xiàn)的呢?
看下源碼:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {

/**
 * Creates a new inheritable thread-local variable.
 */
public InheritableThreadLocal() {
}

/**
 * Computes the initial value of this thread-local variable for the child
 * thread given the parent thread's value. Called from the parent thread when
 * creating a child thread. The default implementation returns the parent
 * thread's value.
 *
 * @param parentValue the value of the variable in the parent thread.
 * @return the initial value of the variable for the child thread.
 */
protected T childValue(T parentValue) {
  return parentValue;
}

@Override
Values values(Thread current) {
  return current.inheritableValues;
}

@Override
Values initializeValues(Thread current) {
  return current.inheritableValues = new Values();
}
}

很明顯InheritableThreadLocal重寫了兩個(gè)方法:

Values values(Thread current)方法返回了Thread類中的成員變量inheritableValues。

Values initializeValues(Thread current)也是new的對(duì)象也是指向inheritableValues。

而ThreadLocal中都是指向的localValues這個(gè)變量。

也就是說當(dāng)我們調(diào)用set(T value)方法時(shí),根據(jù)前面的分析,其實(shí)初始化的是這個(gè)inheritableValues,那么既然子線程能夠獲取到保存的對(duì)象,那我們看看這個(gè)變量在Thread類中哪里有調(diào)用,搜索下就看到:

private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
  ...

  // Transfer over InheritableThreadLocals.
  if (currentThread.inheritableValues != null) {
    inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);
  }

  // add ourselves to our ThreadGroup of choice
  this.group.addThread(this);
}

在Thread類中的create方法中可以看到,該方法在Thread構(gòu)造方法中被調(diào)用,如果currentThread.inheritableValues不為空,就會(huì)將它傳遞給Values的有參構(gòu)造:

 /**
   * Used for InheritableThreadLocals.
   */
  Values(Values fromParent) {
    this.table = fromParent.table.clone();
    this.mask = fromParent.mask;
    this.size = fromParent.size;
    this.tombstones = fromParent.tombstones;
    this.maximumLoad = fromParent.maximumLoad;
    this.clean = fromParent.clean;
    inheritValues(fromParent);
  }

這里可以看到將inheritableValues的值完全復(fù)制過來了,所以我們?cè)谧泳€程一樣可以獲取到保存的變量,我們的分析就到此為止吧。

自己總結(jié)的肯定有很多紕漏,還請(qǐng)大家多多指正。

 感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

相關(guān)文章

最新評(píng)論