同步静态方法如何在 Java 中工作,我可以使用它来加载 Hibernate 实体吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/578904/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do synchronized static methods work in Java and can I use it for loading Hibernate entities?
提问by tomato
If I have a util class with static methods that will call Hibernate functions to accomplish basic data access. I am wondering if making the method synchronized
is the right approach to ensure thread-safety.
如果我有一个带有静态方法的 util 类,它将调用 Hibernate 函数来完成基本的数据访问。我想知道制作该方法synchronized
是否是确保线程安全的正确方法。
I want this to prevent access of info to the same DB instance. However, I'm now sure if the following code are preventing getObjectById
being called for all Classes when it is called by a particular class.
我希望这样可以防止访问同一数据库实例的信息。但是,我现在确定以下代码在getObjectById
被特定类调用时是否会阻止所有类被调用。
public class Utils {
public static synchronized Object getObjectById (Class objclass, Long id) {
// call hibernate class
Session session = new Configuration().configure().buildSessionFactory().openSession();
Object obj = session.load(objclass, id);
session.close();
return obj;
}
// other static methods
}
采纳答案by OscarRyz
By using synchronized on a static method lock you will synchronize the class methods and attributes( as opposed to instance methods and attributes )
通过在静态方法锁上使用同步,您将同步类方法和属性(与实例方法和属性相反)
So your assumption is correct.
所以你的假设是正确的。
I am wondering if making the method synchronized is the right approach to ensure thread-safety.
我想知道使方法同步是否是确保线程安全的正确方法。
Not really. You should let that work do your RDBMS instead. They are good at this kind of stuff.
并不真地。您应该让这项工作来代替您的 RDBMS。他们擅长这种东西。
The only thing you will get by synchronizing the access to the database is to make your application terribly slow. Further more, in the code you posted you're building a Session Factory each time, that way, your application will spend more time accessing the DB than performing the actual job.
通过同步对数据库的访问,您唯一会得到的就是使您的应用程序非常缓慢。此外,在您发布的代码中,您每次都在构建一个会话工厂,这样,您的应用程序将花费更多的时间访问数据库而不是执行实际工作。
Imagine the following scenario:
想象以下场景:
Client A and B attempt to insert different information into record X of table T.
客户端 A 和 B 尝试将不同的信息插入表 T 的记录 X。
With your approach the only thing you're getting is to make sure one is called after the other, when this would happen anyway in the DB, because the RDBMS will prevent them from inserting half information from A and half from B at the same time. The result will be the same but only 5 times ( or more ) slower.
使用您的方法,您得到的唯一方法是确保一个接一个地调用,而这无论如何都会在数据库中发生,因为 RDBMS 将阻止它们同时插入来自 A 的一半信息和来自 B 的一半信息. 结果将相同但仅慢 5 倍(或更多)。
Probably it could be better to take a look at the "Transactions and Concurrency"chapter in the Hibernate documentation. Most of the times the problems you're trying to solve, have been solved already and a much better way.
看一下Hibernate 文档中的“事务和并发”一章可能会更好。大多数情况下,您试图解决的问题已经得到解决,而且是一种更好的方法。
回答by starblue
Static methods use the class as the object for locking, which is Utils.class for your example. So yes, it is OK.
静态方法使用该类作为锁定对象,在您的示例中为 Utils.class。所以是的,没关系。
回答by Ray Lu
If it is something to do with the data in your database, why not utilize database isolation locking to achieve?
如果是和你数据库中的数据有关,为什么不利用数据库隔离锁来实现呢?
回答by David Z
To answer your question, yes it does: your synchronized
method cannot be executed by more than one thread at a time.
要回答您的问题,是的:您的synchronized
方法一次不能由多个线程执行。
回答by oxbow_lakes
Why do you want to enforce that only a single thread can access the DB at any one time?
为什么要强制执行在任何时候只有一个线程可以访问数据库?
It is the job of the database driverto implement any necessary locking, assuming a Connection
is only used by one thread at a time!
数据库驱动程序的工作是实现任何必要的锁定,假设一次Connection
只被一个线程使用!
Most likely, your database is perfectly capable of handling multiple, parallel access
最有可能的是,您的数据库完全有能力处理多个并行访问
回答by Scott Stanchfield
To address the question more generally...
为了更一般地解决这个问题......
Keep in mind that using synchronized on methods is really just shorthand (assume class is SomeClass):
请记住,在方法上使用同步实际上只是速记(假设类是 SomeClass):
synchronized static void foo() {
...
}
is the same as
是相同的
static void foo() {
synchronized(SomeClass.class) {
...
}
}
and
和
synchronized void foo() {
...
}
is the same as
是相同的
void foo() {
synchronized(this) {
...
}
}
You can use any object as the lock. If you want to lock subsets of static methods, you can
您可以使用任何对象作为锁。如果你想锁定静态方法的子集,你可以
class SomeClass {
private static final Object LOCK_1 = new Object() {};
private static final Object LOCK_2 = new Object() {};
static void foo() {
synchronized(LOCK_1) {...}
}
static void fee() {
synchronized(LOCK_1) {...}
}
static void fie() {
synchronized(LOCK_2) {...}
}
static void fo() {
synchronized(LOCK_2) {...}
}
}
(for non-static methods, you would want to make the locks be non-static fields)
(对于非静态方法,您可能希望将锁设为非静态字段)
回答by prasad
static synchronized
means holding lock on the the class's Class
object
where as
synchronized
means holding lock on that class's object itself. That means, if you are accessing a non-static synchronized method in a thread (of execution) you still can access a static synchronized method using another thread.
static synchronized
表示对类的Class
对象synchronized
持有锁,而 as
表示对类的对象本身持有锁。这意味着,如果您在(执行)线程中访问非静态同步方法,您仍然可以使用另一个线程访问静态同步方法。
So, accessing two same kind of methods(either two static or two non-static methods) at any point of time by more than a thread is not possible.
因此,在任何时间点通过多个线程访问两种相同类型的方法(两个静态方法或两个非静态方法)是不可能的。
回答by Vlad Mihalcea
How the synchronized
Java keyword works
如何在synchronized
Java的关键字作品
When you add the synchronized
keyword to a static method, the method can only be called by a single thread at a time.
当您将synchronized
关键字添加到静态方法时,该方法一次只能被单个线程调用。
In your case, every method call will:
在您的情况下,每个方法调用都将:
- create a new
SessionFactory
- create a new
Session
- fetch the entity
- return the entity back to the caller
- 创建一个新的
SessionFactory
- 创建一个新的
Session
- 获取实体
- 将实体返回给调用者
However, these were your requirements:
但是,这些是您的要求:
- I want this to prevent access to info to the same DB instance.
- preventing
getObjectById
being called for all classes when it is called by a particular class
- 我希望这样可以防止访问同一数据库实例的信息。
- 防止
getObjectById
在特定类调用时被所有类调用
So, even if the getObjectById
method is thread-safe, the implementation is wrong.
所以,即使getObjectById
方法是线程安全的,实现也是错误的。
SessionFactory
best practices
SessionFactory
最佳实践
The SessionFactory
is thread-safe, and it's a very expensive object to create as it needs to parse the entity classes and build the internal entity metamodel representation.
它SessionFactory
是线程安全的,创建它是一个非常昂贵的对象,因为它需要解析实体类并构建内部实体元模型表示。
So, you shouldn't create the SessionFactory
on every getObjectById
method call.
所以,你不应该SessionFactory
在每个getObjectById
方法调用上创建。
Instead, you should create a singleton instance for it.
相反,您应该为它创建一个单例实例。
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
The Session
should always be closed
该Session
应始终关闭
You didn't close the Session
in a finally
block, and this can leak database resources if an exception is thrown when loading the entity.
您没有关闭Session
infinally
块,如果加载实体时抛出异常,这可能会泄漏数据库资源。
According to the Session.load
method JavaDocmight throw a HibernateException
if the entity cannot be found in the database.
根据Session.load
方法 JavaDoc可能会抛出 aHibernateException
如果在数据库中找不到实体。
You should not use this method to determine if an instance exists (use
get()
instead). Use this only to retrieve an instance that you assume exists, where non-existence would be an actual error.
您不应使用此方法来确定实例是否存在(
get()
改为使用)。仅用于检索您假定存在的实例,其中不存在将是实际错误。
That's why you need to use a finally
block to close the Session
, like this:
这就是为什么您需要使用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();
}
}
}
Preventing multi-thread access
防止多线程访问
In your case, you wanted to make sure only one thread gets access to that particular entity.
在您的情况下,您希望确保只有一个线程可以访问该特定实体。
But the synchronized
keyword only prevents two threads from calling the getObjectById
concurrently. If the two threads call this method one after the other, you will still have two threads using this entity.
但是synchronized
关键字只能防止两个线程getObjectById
同时调用。如果两个线程一个接一个调用这个方法,你仍然会有两个线程使用这个实体。
So, if you want to lock a given database object so no other thread can modify it, then you need to use database locks.
因此,如果您想锁定给定的数据库对象,以便其他线程无法修改它,那么您需要使用数据库锁。
The synchronized
keyword only works in a single JVM. If you have multiple web nodes, this will not prevent multi-thread access across multiple JVMs.
该synchronized
关键字仅适用于单个 JVM。如果您有多个 Web 节点,这不会阻止跨多个 JVM 的多线程访问。
What you need to do is use LockModeType.PESSIMISTIC_READ
or LockModeType.PESSIMISTIC_WRITE
while applying the changes to the DB, like this:
您需要做的是使用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();
}
}
So, this is what I did:
所以,这就是我所做的:
- I created a new
EntityTransaction
and started a new database transaction - I loaded the
Post
entity while holding a lock on the associated database record - I changed the
Post
entity and committed the transaction - In the case of an
Exception
being thrown, I rolled back the transaction
- 我创建了一个新的
EntityTransaction
并开始了一个新的数据库事务 - 我
Post
在关联数据库记录上持有锁的同时加载了实体 - 我更改了
Post
实体并提交了交易 - 在
Exception
被抛出的情况下,我回滚了事务
For more details about ACID and database transactions, check out this articleas well.
有关 ACID 和数据库事务的更多详细信息,还可以查看这篇文章。