我有一个关于同时访问的问题。我正在尝试实现自己的调度程序。我所拥有的是一个由服务器调用的triggerClass(实际上它是一个石英作业,但这不是必须留下来的)。 Trigger注册了他想要运行到Control Center的愿望,Control Center为该特殊作业创建一个Controller类并将其存储在队列中。如您所见,Control Center是一个单例类,并使用一些同步方法:
getInstanceController(...) // gets the ( already existing ) controller for the JobInstance**
registerInstance(...) // registers a firing desire for a job (note: on job could fire multiple times using the same Controller instance)**
tryStart(...) // looks if a job can be executed now. E.g. does nothing if another controller is already running.
注意:每次调用registerInstance时都会执行tryStart,并在当前运行的作业结束时在startNextJobRun()结束时调用它。
如此。我的第一个问题是:我是否认为方法getInstanceController(...)的同步修饰符是超流体的?它从来没有从外部JobInstanceControlCenter调用(我实际上遇到了N * ullPointerException *因为在tryStartMethod中他将所有三个控制器从队列中拉出来并覆盖了activeInstanceControllers-map中的条目。当作业回来时他们试图找到他们的控制器使用已经消失的getInstanceController(...)。)
我的下一个问题是:
我尝试在我的服务器启动时应用3个JobTriggers(每个都在一个单独的线程中)。 (我向服务器注册了一个mBean,它创建了一个石英调度程序,并将这些作业注册为现在正在运行)当我这样做时,所有三个线程同时运行,最终同时运行同步方法,这是不应该发生的。
我尝试应用这些JobTriggers以完全相同的标称时间开始,但未来8秒,结果如预期的那样。 3同时触发启动器。第一个线程锁定同步方法,其他线程等待第一个完成。
如果我误解了有关同步的内容,有什么建议吗?
public class JobTrigger
{
public void execute(SomeContext context)
{
JobInstanceControlCenter.getInstance().registerInstance(context);
}
}
public class JobInstanceControlCenter
{
private static JobInstanceControlCenter _instance = null;
private final Map<JobType, PriorityQueue<JobInstanceController>> queuedInstanceControllers;
private final Map<JobType, JobInstanceController> activeInstanceControllers;
private JobInstanceControlCenter()
{
// prevent instantiation
queuedInstanceControllers = Collections.synchronizedMap(new HashMap<JobType, PriorityQueue<JobInstanceController>>());
activeInstanceControllers = Collections.synchronizedMap(new HashMap<JobType, JobInstanceController>());
}
static final JobInstanceControlCenter getInstance()
{
if (_instance == null)
_instance = new JobInstanceControlCenter();
return _instance;
}
synchronized JobInstanceController getInstanceController(JobInstanceKey JobInstanceKey)
{
/**
* momentary active Controller
*/
if (activeInstanceControllers.get(JobInstanceKey.getJobType()) != null
&& activeInstanceControllers.get(JobInstanceKey.getJobType()).getJobInstanceKey().equals(JobInstanceKey))
{
if (JobScheduler.getInstance().getSchedulerVO().getIsEnabledLogging())
logger.info("reuse active JobInstanceController: " + JobInstanceKey);
return activeInstanceControllers.get(JobInstanceKey.getJobType());
}
/**
* momentary queyed Controllers
*/
PriorityQueue<JobInstanceController> queue = queuedInstanceControllers.get(JobInstanceKey.getJobType());
if (queue != null && !queue.isEmpty())
for (JobInstanceController controller : queue)
if (controller.getJobInstanceKey().equals(JobInstanceKey))
{
if (JobScheduler.getInstance().getSchedulerVO().getIsEnabledLogging())
logger.info("get next queyed JobInstanceController: " + JobInstanceKey);
return controller;
}
return null;
}
synchronized void registerInstance(JobExecutionContext context)
{
JobInstanceKey JobInstanceKey = JobInstanceKey.getJobInstanceKey(context);
PriorityQueue<JobInstanceController> queue = queuedInstanceControllers.get(JobInstanceKey.getJobType());
JobInstanceController instanceController = getInstanceController(JobInstanceKey);
if (instanceController == null)
{
instanceController = JobInstanceController.createInstance(JobInstanceKey);
if (queue == null)
queue = new PriorityQueue<JobInstanceController>();
queue.offer(instanceController);
queuedInstanceControllers.put(JobInstanceKey.getJobType(), queue);
}
instanceController.registerNewJobRun(context);
tryStart(JobInstanceKey.getJobType());
}
synchronized void tryStart(JobType jobType)
{
try
{
JobInstanceController activeController = activeInstanceControllers.get(jobType);
if (activeController != null)
{
if (activeController.getJobInstanceState() == JobInstanceState.RUNNING)
{
if (JobScheduler.getInstance().getSchedulerVO().getIsEnabledLogging())
logger.info("Active Controller was already running. Job aborted.");
return; // will be reconsidered when running job finishes
}
// note: startNextJobRun() will run synchronous
boolean hadAnotherRun = activeController.startNextJobRun(); // true if next Job started, false if Controller was empty.
if (!hadAnotherRun)
{
finishedJobInstanceControllers.put(activeController.getJobInstanceKey(), activeController);
activeController = null; // finish empty Controller
}
}
if (activeController == null) //either ativeController had been initially null, or it was set due to beeing empty
{
activeController = queuedInstanceControllers.get(jobType).poll();
activeInstanceControllers.put(jobType, activeController);
boolean hadAnotherRun = activeController.startNextJobRun();
}
}
catch(ControllerException e) // some homemade Exception thrown by activeController.startNextJobRun()
{
logger.error("", e);
}
}
}
答案 0 :(得分:0)
您的单件工厂方法未同步,请尝试:
static final synchronized JobInstanceControlCenter getInstance(){
您还可以尝试更高级的内容,例如仔细检查锁定,但请小心,因为它可能很棘手,只适用于最近的jdk。 Google可以提供大量关于双重检查锁定的信息。
现在发生的事情是你的线程并发运行,因此每个线程都抓取它自己的JobInstanceControlCenter实例。由于您的其他同步方法是实例方法,因此它们在JobInstanceControlCenter的实例上同步。
答案 1 :(得分:0)
看起来你没有理由懒洋洋地创建JobInstanceControlCenter单例。
只做
private static final JobInstanceControlCenter _instance = new JobInstanceControlCenter();
然后您不必担心同步的getter或尝试双重检查锁定。除非你故意试图推迟加载,否则这是简单和无错误的单例模式。