如何在synchronized
Java的关键字作品
将synchronized
关键字添加到静态方法时,该方法一次只能由单个线程调用。
就您而言,每个方法调用将:
- 创建一个新的
SessionFactory
- 创建一个新的
Session
- 获取实体
- 将实体返回给调用者
但是,这些是您的要求:
- 我希望这样做可以防止访问同一数据库实例的信息。
- 防止
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();
}
}
}
防止多线程访问
对于您的情况,您想确保只有一个线程可以访问该特定实体。
但是该synchronized
关键字仅阻止两个线程getObjectById
同时调用。如果两个线程一个接一个地调用此方法,则仍将有两个线程使用此实体。
因此,如果要锁定给定的数据库对象,以使其他线程无法修改它,则需要使用数据库锁。
该synchronized
关键字仅在单个JVM中有效。如果您有多个Web节点,则不会阻止跨多个JVM的多线程访问。
您需要做的是在数据库中使用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和数据库事务的更多详细信息,也请参阅本文。