如何在播放框架中多次停止运行单例类
我的 Play 应用程序中有一个单例类。这个单例类是一个很长的过程,它将从消耗大量内存的数据库生成报告。当我在开发模式下运行应用程序时,此单例功能会执行多次。我希望此功能仅运行一次。我应该为此做什么?
我的代码是:
public class DataGridManagerImpl extends ComponentContainer implements DataGridManager {
private static DataGridManager instance = null;
private DataGridManagerImpl(){
load();
}}
@Override
public void load() {
//Myreports function
}
public static DataGridManager getInstance(){
if (instance == null){
instance = new DataGridServiceManagerImpl();
}
return instance;
}
}
在我的控制器文件中的模板函数中
DataGridManager dataGridMgr = DataGridManagerImpl.getInstance();
如果我访问该页面,它将再次执行加载报告函数。
I have a singleton class in my play app. This singleton class is a long process which will generate reports from DB which consumes huge amount of memory. When i run my application in dev mode this singleton functionality is executing several times. I want this functionality to run only once. What should I do for that?
My code is:
public class DataGridManagerImpl extends ComponentContainer implements DataGridManager {
private static DataGridManager instance = null;
private DataGridManagerImpl(){
load();
}}
@Override
public void load() {
//Myreports function
}
public static DataGridManager getInstance(){
if (instance == null){
instance = new DataGridServiceManagerImpl();
}
return instance;
}
}
In my controller file inside a template function
DataGridManager dataGridMgr = DataGridManagerImpl.getInstance();
If i access the page it is executing the load reports function again.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果没有代码解释你是如何创建类的,就很难回答。据我了解,您想要的是仅运行一个进程一次。
可能最好的方法是使用计划作业。这将在某个时间触发该进程,并且 Play 确保该进程只有 1 个实例同时运行,即使计划表明必须运行另一个实例。假设您每小时安排一个流程,该流程需要 3 小时。初始进程将是唯一一个运行 3 小时直至完成的进程。
现在,我假设您希望您的流程在生成报告时重复出现。如果没有,如果您只想运行一次,那么您可能需要使用 相反,异步引导作业。这只会在应用程序开始时运行一次。
更新时编辑:在开发过程中,@OnApplicationStart 可能会执行多次,因为当您进行某些代码更改时,Play 可能会自动重新加载应用程序。这是开发流程的一部分(与 @OnApplicationStart 作业在服务器收到请求之前不会在 Dev 中启动相同)。
由于这是一个您只想运行一次的作业,因此您可以尝试使用以下检查在开发模式下跳过它:
if(Play.mode == Play.Mode.DEV)
如果您需要至少运行一次,请添加您可以在开发期间访问以启动进程的仅限开发的 url。
现在,在您的更新中,您还提到您正在控制器中调用该代码,并且每次访问控制器时都会调用该方法。这是预期的。 Singleton并不意味着它只会运行一次,而是意味着系统中只有1个对象。如果在控制器中启动计算,则每次访问控制器时都会发生计算。
第二次编辑(关于评论): Arasu,另一个问题是您在构造对象时调用方法 load() 。单例不能保证对象只会被构造一次。它保证一旦构建,就只会存在 1 个对象。但有可能发生该对象被 GC 删除的情况,在这种情况下,根据您的代码,如果您再次构造它,那么您将调用 load() 并重做处理。
最好的解决方案是不在构造函数上调用“load”,而是强制用户(您)在检索实例后调用它。另一种方法是在加载开始时设置一些标志来检测代码是否已运行。请注意,Play 是无状态的,因此该标志需要存储在数据库中。
Without code explaining how did you create your class it's hard to answer. From what I understand what you want is to run a process only once.
Problably the best approach is to use a Scheduled Job. This will trigger the process at a certain time, and Play ensures that only 1 instance of this process is running at the same time, even if the schedule would indicate another instance has to run. Let's say you have a process scheduled every hour and the process takes 3 hours. The initial process will be the only one running for 3 hours until it finishes.
Now, I would assume you want your process to be recurring as it generate reports. If not, if you only want to run it once, then you may want to use an asynchronous bootstrap job instead. This would run just once, at the beginning of the application.
EDIT on update: during development the @OnApplicationStart may execute several times, as Play may automatically reload the application when you do certain code changes. This is part of the dev process (the same that an @OnApplicationStart job won't start in Dev until the server gets a request).
As it's a job that you only want to run once, you may try to skip it in dev mode using the check:
if(Play.mode == Play.Mode.DEV)
If you need to run it at least once, add a dev-only url that you can access during dev to start the process.
Now, on your update you also mention that you are calling that code in a controller, and that every time the controller is acessed the method is called. That's expected. Singleton doesn't mean that it will run only once, but that there is only 1 object in the system. If in your controller you launch the calculation, that will happen everytime you access the controller.
SECOND EDIT (on comments): Arasu, the other issue is that you are calling the method load() when you construct the object. A singleton doesn't garantee that the object will only be constructed once. It garantees that, once constructed, only 1 object will exist. But it may happen that the object is removed by GC, in this case as per your code if you construct it again then you'll call load() and redo the processing.
The best solution is to not call "load" on constructor, but to force the user (you) to call it after retrieving the instance. An alternative is to set some flag at the beginning of load that detects if the code has been run. Be aware that Play is stateless, so that flag will need to be stored in the database.
单例的定义是它只能运行一次,这实际上是模式的本质。如果您以某种方式设法多次运行它,则您的单例中可能会出现实现错误。
重新检查 Wikipedia 中的单例模式。
编辑:
此代码使得无法获取多个实例。怎样才能得到不止一个呢?
或者您的意思是实例化 Singleton 类,而不是调用 Singleton.getInstance() ?
the defition of a singleton is that it can run only once, it's practically the nature of the pattern. If you somehow manage to run it multiple times, you might have implementation errors in your singleton.
Recheck the singleton pattern in Wikipedia.
Edit:
This code makes it impossible to fetch more than one instance. How would you get more than one?
Or do you mean that you instanciate the Singleton class, instead of calling Singleton.getInstance()?
有可能让 Singleton 进行耗时的处理并同时被两个不同的线程调用。我想这就是这里的情况。程序中会多次调用同一个 Singleton 对象的方法。
我运行了一个小测试...两个线程调用同一个 Singleton 对象,这是结果
,这是代码。
如果我的想法有误,请纠正我上面的想法。
因此,您需要一个变量来跟踪耗时过程的状态(即布尔值 isRunning)或该过程被调用的时间。
您还可以使单例的相关耗时方法同步,这样只有一个线程可以在该方法运行时访问该方法(在我的示例中,如果您使 doTimeConsumingThing() 同步,则第二个线程将阻塞,直到从单例调用该方法为止。 已完成。
第一个线程
It is possible to have a Singleton doing a time consuming processing and be called the same time by two different threads. I think this is the situation here. The same Singleton object's method is called multiple times from the program.
I have run a little test... two thread calling the same Singleton object and here is the result
and here is the code.
Please correct my notion above if i am wrong.
Hence what you need is a variable to keep track of the state of the time consuming procedure (i.e. a boolean isRunning) or the times the procedure has been called.
You can also make the pertinent time consuming method of the Singleton synchronized so only one thread can access the method while it is running (in my example if you make the doTimeConsumingThing() synchronized, the second thread will block until the singleton's method called from the first thread is finished.
Hope it helps
我在 DEV 模式下遇到了同样的问题,我所做的是为我不想在每个 @OnApplicationStart 运行的任务创建一个模块。
诀窍是在模块中的重写“onLoad()”方法中启动这些任务:
onLoad() 方法仅调用一次,而不是每次重新启动应用程序时调用。
I had the same problem in DEV mode, and what I did is create a module for the tasks I don't want to be run at every @OnApplicationStart.
The trick is to launch those tasks in a overriden "onLoad()" method, in the module:
The onLoad() method is called one time only, not each time the application is restarted.
我不知道这是否有帮助,但这里有一些需要检查的事情:
您问题中的代码不是线程安全的。您在
getInstance
中缺少synchronized
关键字。这可能会导致构造函数被不同的线程多次调用。DataGridManagerImpl
是否可以由不同的类加载器加载?该静态实例变量对于整个 JVM 来说不是静态的,只是对于该类的类加载器来说是静态的。load
是public
。是否有其他代码正在调用该方法?I don't know if this will help, but here are some things to check:
The code in your question is not thread-safe. You're missing the
synchronized
keyword ingetInstance
. That could cause the constructor to be called more than once by different threads.Could
DataGridManagerImpl
be getting loaded by different classloaders? That staticinstance
variable isn't static for the whole JVM, just static for that class' classloader.load
ispublic
. Could some other code being calling that method?