スレッドローカル変数(ThreadLocalの)原理の分析

まず、ThreadLocalの役割の
1.1ソース
    すべてが、彼らは自分自身の根本的な原因を持って、偶然ではないものがある、そのソースを持っています。日常的に使用するためのツール、その起源としてThreadLocalのJava開発者は、それは何ですか?主にローカル変数でのJava並行性の問題を解決するための手段として、ThreadLocalの出現は、同時にアクセス共有変数が発生し、複数のスレッドによって引き起こされる問題を回避するために、スレッド間のリソースの分離を可能にする、スレッドを格納します。避ける共有:手段は同時使用の課題を解決ThreadLocalのことがわかります。

1.2簡単なアプリケーション
    のThreadLocalはどのようにそれを使用すること?各リクエストが入ってくる時に簡単な例では、そのようなTomcatのベースのWebサーバーは、各要求は、インターセプタを介して、実行の別のスレッドによって処理され、この要求のユーザ名、 ThreadLocalのに格納されたセッション情報は、この情報は、使用中の論理コードを任意にThreadLocalから採取することができます。

第二に、ThreadLocalの原則の実現
2.1 ThreadLocalの実装の原則は推測
    研究ThreadLocalのソースの前には、あなたは、単に達成推測することができ、その基礎となる(実際には、ソースコードの一部を読む前に、あなたは基本的な実装に応じてその機能を推測して、ソースコードを見てすることができます彼の疑惑を検証します)。ThreadLocalの役割は、各スレッドが異なる変数を持っていることですので、それは地図、キースレッド内にThreadLocalに格納することができ、値を使用すると、各スレッドは、独自の価値はそれを持って行うことができ、変数でした。図示のように:

  

2.2推測検証
    中に発見、ThreadLocalのクラスのビューJDK1.8のバージョンをThreadLocalMapという名前の地図を持っている(それは内部クラスです)が、そのようなオブジェクトを保持するために行い、この中に定義されたThreadクラス中(Threadクラスでありますプロパティ:ThreadLocal.ThreadLocalMapのThreadLocal = null)の代わりにThreadLocalクラス。構造に示すように、実際には実際には、上記の予想の試合で実装:

  

    実装の分析のみプロキシツールThreadLocalのクラスを発見し、スレッドに関連する任意のデータを保持していない、その内部には、データに関連付けられたスレッドが雌ねじに格納されています。親和性の観点からのデータから、ThreadLocalMapは本当により合理的になるスレッドに属している必要があります。もちろん、もっと重要な理由は、スレッド参照の地図でThreadLocalのは、限りThreadLocalのオブジェクトは、(多くの場合、アプリケーションのライフサイクル全体を通じて)が存在するとして、スレッドオブジェクトは、その結果、再利用することができないので、2.1の実装は、メモリリークになるということですメモリリーク。

    値のマッピングを維持するためにThreadLocalMapクラスThreadLocalのは、単に地図を採用したが、エントリの使用、およびThreadLocalのか弱いの上に(弱参照のこの単純な理解を行うことができません。GCが発生したときに、かどうかに関係なく十分なメモリの、唯一の弱い参照です次のようにオブジェクトは、)を回収されます。

  

    なぜ弱参照を使うのか?または、なぜ強い参照それを使わないのでしょうか?我々はThreadLocalのオブジェクトを必要としないときの強い参照を、その参照がnullに設定されている場合(つまり、もはやそれが回復するために、次のGCまでにない、オブジェクト参照を保持していない)が、 ThreadLocalMapオブジェクトもThreadLocalのオブジェクトへの強い参照を保持している場合、ThreadLocalのオブジェクトを復元することはできません。あなたがスレッドプールを使用している場合は、ThreadLocalMapは、いくつかのThreadLocalのスレッドが保持しているメモリリークで、その結果、より多くなります。

    メモリリークを避けるためにしようとするのJava実装が、それはまだメモリリークがあるかもしれない実際にあります。

三、ThreadLocal内存泄漏
3.1 内存泄漏的原因    
    ThreadLocal发生内存泄漏的地方也是ThreadLocalMap,可以从上文的代码看到,ThreadLocalMap中对ThreadLocal是弱引用,对value是强引用,当ThreadLocal对象被回收后,虽然对应对应Entry对象的key所引用的对象以被回收,但是它还持有value对象的强引用,导致value无法释放,value一直增加也会造成内存泄漏。

    那么有人会说,既然对value强引用会造成value无法回收,从而引起内存泄漏,为什么不干脆设计成和ThreadLocal一样,使用弱引用呢?上文提到,如果一个对象只被弱引用,在GC的时候就会被回收,那么就会出现这么一种情况,明明往ThreadLocal中设置了值,取出来的时候却是null,这就是因为GC的时候把value对象回收掉导致的。所以,ThreadLocalMap对value如果是弱引用的话,会导致从ThreadLocal中拿到的值发生错乱。

    那么又有人会问了,既然GC的时候value会因为弱引用被回收,为什么ThreadLocal不会被回收呢?实际上ThreadLocal对象也可能被回收,但是JDK官方建议使用ThreadLocal时将其定义为静态变量,对ThreadLocal对象始终持有强引用,使其在GC时不会被回收。

3.2 如何应对内存泄漏
    每次使用完ThreadLocal都调用其remove()方法,其中会对Entry中的该ThreadLocal对象及其对应的value进行清除:

  

    当然,JDK的开发者不会将所有希望寄托于使用者在每次使用完ThreadLocal后都调用remove()方法来避免内存泄漏,他们在很多地方都埋下一个逻辑:清理key=null的Entry中对应的value。比如最常用的ThreadLocal.set()方法,会查找出下一个key=null的Entry,然后对其value进行清理,源码如下:

  

    另外,在ThreadLocal的get()、remove()方法中也存在相似的逻辑。当然,在最极端的情况下,不调用get()、set()、remove()方法,那么就无法清除那些key=null的Entry中的value了。

四、正确使用ThreadLocal
正确用法:    

1.为了避免弱引用导致GC时ThreadLocal对象被回收,官方建议将ThreadLocal定义为静态变量,这样就会对该ThreadLocal对象一直持有一个强引用,使其不会在GC时被意外回收,从而导致ThreadLocal数据丢失。

2.使用try-finally的方式来使用ThreadLocal,在finally中调用ThreadLocal对象的remove()方法,清理对应的数据。

五、扩展
    Spring中bean都是默认单例的,在并发的情况下对共享变量的操作会存在线程安全问题,Spring为了解决这个问题,就使用了ThreadLocal。

    要想实现JDBC事务, 就必须是在同一个连接对象中操作(多个连接下事务就会不可控, 需要借助分布式事务完成)。那Spring 如何保证数据库事务在同一个连接下执行的呢?DataSourceTransactionManager 是Spring的数据源事务管理器, 它会在你调用getConnection()的时候从数据库连接池中获取一个connection, 然后将其与ThreadLocal绑定, 事务完成后解除绑定。这样就保证了事务在同一连接下完成。

  

  

おすすめ

転載: www.cnblogs.com/TheMambaMentality/p/11964589.html