利用シナリオ
私たちは、データベース接続の管理区分があるとします。
class ConnectionManager {
private static Connection connect = null;
private static String url = System.getProperty("URL");
public static Connection openConnection() {
if(connect == null){
try {
connect = DriverManager.getConnection(url);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connect;
}
public static void closeConnection() {
if(connect!=null) {
try {
connect.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
クラスは、マルチスレッド環境で使用されている場合は、スレッド安全性の問題が存在します、次の2つのメソッドsynchronizedキーワード同期を追加することができますが、それは非常にプログラムのパフォーマンスが低下しますローカル変数の接続にもを回すことができます:
class ConnectionManager {
private Connection connect = null;
public Connection openConnection(String url) {
if(connect == null){
try {
connect = DriverManager.getConnection(url);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connect;
}
public void closeConnection() {
if(connect!=null) {
try {
connect.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
class ConnectionManagerTest {
private String url = System.getProperty("URL");
public void insert() {
ConnectionManager connectionManager = new ConnectionManager();
Connection connection = connectionManager.openConnection(this.url);
//使用connection进行操作
connectionManager.closeConnection();
}
public void update() {
ConnectionManager connectionManager = new ConnectionManager();
Connection connection = connectionManager.openConnection(this.url);
//使用connection进行操作
connectionManager.closeConnection();
}
}
新しいデータベース接続を作成するために、各CURD方法は次の2つのソリューションを持つことができるデータベース、に大きな圧力が発生します。
-
接続を管理するために使用する接続プールは、どちらも接続を破壊し、たびに作成されていないが、接続プールからの融資利用可能な接続は、それはリターンを使い果たします。
- その後、データベース接続を管理するためにThreadLocalを使用し、各スレッドは避け同期の問題、データベースへの圧力を減らすために同じ接続を共有するための雌ねじの願いに、独自の独立した接続希望を持っている:それは、接続が確立された最高のはこれです、ここで見ることができます最良の選択。これは、スレッドごとに独自の接続を維持し、スレッド内で共有することができます。
class ConnectionManager {
private static String url = System.getProperty("URL");
private static ThreadLocalconnectionHolder = ThreadLocal.withInitial(() -> {
try {
return DriverManager.getConnection(url);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
});
public static Connection openConnection() {
return connectionHolder.get();
}
public static void closeConnection() {
Connection connect = connectionHolder.get();
if(connect!=null) {
try {
connect.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
また、他のすべてのスレッド管理は、リソースのローカルコピーのコピーが必要に使用することができます:JavaでのThreadLocalの入門
原則
オブジェクトの3種類を含むこのマッピング:各スレッドがリソースのコピーを有することになるので、スレッドのThreadLocalの特定の含有量は、存在し-ThreadLocalオブジェクト、次いでThreadLocalのオブジェクトからのマッピングに格納されたコンテンツは、天然に存在するスレッドのオブジェクトを:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalのクラスは唯一の地図にアクセスするためのインタフェースを提供します。
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();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
これは、内部エントリ配列の添字を維持する機能類似ハッシュマップは、threadLocalHashCode ThreadLocalのオブジェクトによって計算される器具が来ることThreadLocalMap ThreadLocalの内部クラスです。エントリは、弱参照にThreadLocalでキーを達成するために、弱い参照から継承されました:
static class Entry extends WeakReference<threadlocal
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
</threadlocal
図のメモリモデルは以下のように:
REF ThreadLocalの場合スタックは、エントリThreadLocalMap ThreadLocalの参照は弱くしか、そうThreadLocalのオブジェクトが回収されるので、入力キーがnullになり、その値/セット/削除ThreadLocalMap取得するたびに、それが自動的になりますNULL値にクリーンアップの鍵は、この値も回収することができます。
注:ThreadLocalの文献がポップしていない場合ので、ここで仮想基準は、名前に存在していますで、スレッドと同じライフサイクルを持っている、(例えばconnectionHolder上記のように、通常我々はその患者さんとのThreadLocal単一グローバルにアクセス可能なので、セットの静的を確認する必要があります)のみので、それに対応する値を削除するには、使用後ThreadLocal.removeを呼び出すことを忘れないでください。
また、ThreadLocalMap ThreadLocalのが唯一の弱い参照であるのため、値のための強力なリファレンスで、ThreadLocalの場合そこには他の強い参照されていないと回復しているため、後にも取得/設定オフ呼び出していない、それは、メモリリークが生成されます
また、再利用されるスレッドプールのスレッドを使用してThreadLocalMap内に保存、その後、再利用される場合は、スレッド間の資源になりますスレッドがスレッドプールに戻りするには、removeメソッドを呼び出すために覚えておくので、絶縁されていません。
ハッシュの競合
ThreadLocalMap上記機能は、代わりに、プロービング方法線形使用する、ハッシュ衝突が(二つの重要なハッシュ値計算オブジェクトを介して同一の配列インデックスを生じる)が発生した場合、それはリストモードを使用しない、その実装類似のHashMapであります競合が発生していないときにのみ、リニアアレイは、空いている位置を検索します。
配列が大きい場合、パフォーマンスが悪くなる、それはThreadLocalの数を制御することをお勧めします。