如何在play框架中多次停止运行singleton类

时间:2011-10-13 13:04:18

标签: java singleton playframework

我的Play应用程序中有一个单例类。这个单例类是一个很长的过程,它将从DB生成报告,消耗大量内存。当我在开发模式下运行我的应用程序时,这个单例功能正在执行多次。我希望此功能只运行一次。我该怎么做?

我的代码是:

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();

如果我访问该页面,它将再次执行加载报告功能。

5 个答案:

答案 0 :(得分:2)

如果没有代码解释你是如何创建课程的,那么很难回答。根据我的理解,你想要的只是运行一次。

可能最好的方法是使用Scheduled Job。这将在特定时间触发进程,并且Play确保此进程的只有1个实例同时运行,即使该计划表明另一个实例必须运行。假设您每小时安排一个流程,此过程需要3个小时。初始过程将是唯一一个运行3小时直到完成的过程。

现在,我假设您希望您的流程在生成报告时重复发生。如果没有,如果您只想运行一次,那么您可能想要使用asynchronous bootstrap job。这只会在应用程序开始时运行一次。

EDIT on update:在开发过程中@OnApplicationStart可能会多次执行,因为当您执行某些代码更改时,Play可能会自动重新加载应用程序。这是开发过程的一部分(与服务器获取请求之前@OnApplicationStart作业无法在Dev中启动相同)。

由于这是一项您只想运行一次的工作,您可以尝试使用检查在开发模式下跳过它:

if(Play.mode == Play.Mode.DEV)

如果您需要至少运行一次,请添加一个dev-only url,您可以在开发期间访问该URL以启动该过程。

现在,在您的更新中,您还提到您在控制器中调用该代码,并且每次控制器被访问时都会调用该方法。这是预期的。 Singleton并不意味着它只运行一次,而是系统中只有一个对象。如果在控制器中启动计算,则每次访问控制器时都会发生这种情况。

第二次编辑(评论时): Arasu,另一个问题是在构造对象时调用方法load()。单身人士并不保证该物体只会被建造一次。它保证,一旦建成,只存在一个物体。但是可能会发生这样的情况,GC会删除该对象,在这种情况下,根据您的代码,如果再次构造它,那么您将调用load()并重做处理。

最好的解决方案是不要在构造函数上调用“load”,而是强制用户(你)在检索实例后调用它。另一种方法是在加载开始时设置一些标志,以检测代码是否已运行。请注意,Play是无状态的,因此该标志需要存储在数据库中。

答案 1 :(得分:1)

单身人士的定义是,它只能运行一次,这实际上是模式的本质。如果您以某种方式设法多次运行它,您的单例中可能会出现实现错误。

重新检查Wikipedia中的单例模式。

编辑:

此代码使得无法获取多个实例。你怎么会得到不止一个?

public class Singleton {
    private static Singleton _instance;

    private Singleton() {  }

    public static synchronized Singleton getInstance() {
            if (null == _instance) {
                    _instance = new Singleton();
            }
            return _instance;
    }
}

或者你的意思是你实例化Singleton类,而不是调用Singleton.getInstance()?

答案 2 :(得分:0)

可能让Singleton执行耗时的处理并由两个不同的线程同时调用。我认为这就是这里的情况。从程序中多次调用相同的Singleton对象的方法。

我已经运行了一些测试...两个线程调用相同的Singleton对象,这是结果

Thread[Thread 1,5,main] internal loop number = 0 Object = example.Singeton@164f1d0d
Thread[Thread 2,5,main] internal loop number = 0 Object = example.Singeton@164f1d0d
Thread[Thread 1,5,main] internal loop number = 1 Object = example.Singeton@164f1d0d

这是代码。

package example;

public class Singeton {

private static final Singeton INSTANCE = new Singeton(); 
private Singeton() {}

public static Singeton getInstance(){
    return INSTANCE;
}

public boolean doTimeConsumingThing(){
    for (int i=0; i<10000000;i++){
        System.out.println(Thread.currentThread() + " internal loop number = " + i +  " Object = "  + toString());
    }
    return true;
}
}

 package example;

public class MulThread extends Thread{
public MulThread(String name) {
    super(name);
}
@Override
public void run() {
    while(true){
        Singeton s = Singeton.getInstance();
        System.out.println("Thread " + getId());
        s.doTimeConsumingThing();
    }
}
public static void main(String[] args) {
    MulThread m1 = new MulThread("Thread 1");
    MulThread m2 = new MulThread("Thread 2");
    m1.start();
    m2.start();
}
}

如果我错了,请纠正我的观点。

因此,您需要的是一个变量来跟踪耗时过程的状态(即布尔值isRunning)或者调用过程的次数。

你也可以使Singleton的相关耗时方法同步,因此只有一个线程可以在它运行时访问该方法(在我的例子中,如果你使doTimeConsumingThing()同步,第二个线程将阻塞直到单例的方法从第一个线程调用完成。

希望有所帮助

答案 3 :(得分:0)

我在DEV模式下遇到了同样的问题,我所做的是为每个@OnApplicationStart都不想运行的任务创建一个模块。

诀窍是在模块中覆盖“onLoad()”方法启动这些任务:

public void onLoad() 
{
    // tasks to run one time only
}

onLoad()方法只调用一次,而不是每次重启应用程序。

答案 4 :(得分:0)

我不知道这是否会有所帮助,但有些事情需要检查:

您问题中的代码不是线程安全的。您错过synchronized中的getInstance关键字。这可能会导致构造函数被不同的线程多次调用。

DataGridManagerImpl可能被不同的类加载器加载吗?对于整个JVM,静态instance变量不是静态的,对于该类的类加载器来说只是静态的。

loadpublic。其他一些代码可以调用该方法吗?

相关问题