どのようにsynchronizedJavaのキーワード作品
synchronized静的メソッドにキーワードを追加すると、そのメソッドは一度に1つのスレッドからのみ呼び出すことができます。
あなたの場合、すべてのメソッド呼び出しは次のようになります:
- 新しいを作成します
SessionFactory
- 新しいを作成します
Session
- エンティティをフェッチする
- エンティティを呼び出し元に返す
ただし、これらはあなたの要件でした:
- これにより、同じDBインスタンスへの情報へのアクセスを防ぐことができます。
- 防止は、
getObjectByIdそれが特定のクラスによって呼び出されたときに、すべてのクラスのために呼び出されています
したがって、getObjectByIdメソッドがスレッドセーフであっても、実装は間違っています。
SessionFactory ベストプラクティス
の SessionFactoryスレッドセーフであり、それはそれはエンティティクラスを解析し、内部実体のメタモデル表現を構築する必要があるとして作成するには、非常に高価な物です。
したがって、SessionFactoryすべてを作成する必要はありませんgetObjectByIdメソッド呼び出しでを。
代わりに、そのためのシングルトンインスタンスを作成する必要があります。
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
の Session必ず閉じる必要があります
あなたはで閉じませんでしSessionたfinallyブロックたため、エンティティの読み込み中に例外がスローされた場合、データベースリソースがリークする可能性があります。
Session.loadメソッドによると、HibernateExceptionエンティティがデータベースで見つからない場合、JavaDocはをスローする可能性があります。
このメソッドを使用してインスタンスが存在するかどうかを判断しないでください(get()代わりに使用してください)。これは、存在しないことが実際のエラーになる、存在すると想定するインスタンスを取得する場合にのみ使用してください。
そのfinallyためSession、次のようにブロックを使用してを閉じる必要があります。
public static synchronized Object getObjectById (Class objclass, Long id) {
Session session = null;
try {
session = sessionFactory.openSession();
return session.load(objclass, id);
} finally {
if(session != null) {
session.close();
}
}
}
マルチスレッドアクセスの防止
あなたの場合、1つのスレッドだけがその特定のエンティティにアクセスできるようにする必要がありました。
ただし、このsynchronizedキーワードは、2つのスレッドがgetObjectById同時にです。2つのスレッドがこのメソッドを順番に呼び出す場合でも、このエンティティを使用する2つのスレッドがあります。
したがって、他のスレッドがオブジェクトを変更できないように特定のデータベースオブジェクトをロックする場合は、データベースロックを使用する必要があります。
synchronizedキーワードは、単一のJVMで動作します。複数のWebノードがある場合、これは複数のJVMにわたるマルチスレッドアクセスを妨げません。
あなたがする必要があるのは、次のように、DBに変更を適用するLockModeType.PESSIMISTIC_READかLockModeType.PESSIMISTIC_WRITE使用することです:
Session session = null;
EntityTransaction tx = null;
try {
session = sessionFactory.openSession();
tx = session.getTransaction();
tx.begin();
Post post = session.find(
Post.class,
id,
LockModeType.LockModeType.PESSIMISTIC_READ
);
post.setTitle("High-Performance Java Perisstence");
tx.commit();
} catch(Exception e) {
LOGGER.error("Post entity could not be changed", e);
if(tx != null) {
tx.rollback();
}
} finally {
if(session != null) {
session.close();
}
}
だから、これは私がやったことです:
EntityTransaction新しいデータベーストランザクションを作成して開始しました
Post関連するデータベースレコードをロックしている間にエンティティをロードしました
Postエンティティを変更してトランザクションをコミットしました
Exceptionスローされた場合、トランザクションをロールバックしました
ACIDとデータベーストランザクションの詳細については、こちらの記事もご覧ください。