java synchronized的问题

时间:2013-12-30 13:43:14

标签: java synchronized

我有一个关于同时访问的问题。我正在尝试实现自己的调度程序。我所拥有的是一个由服务器调用的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);
        }


    }

}

2 个答案:

答案 0 :(得分:0)

您的单件工厂方法未同步,请尝试:

static final synchronized JobInstanceControlCenter getInstance(){

您还可以尝试更高级的内容,例如仔细检查锁定,但请小心,因为它可能很棘手,只适用于最近的jdk。 Google可以提供大量关于双重检查锁定的信息。

现在发生的事情是你的线程并发运行,因此每个线程都抓取它自己的JobInstanceControlCenter实例。由于您的其他同步方法是实例方法,因此它们在JobInstanceControlCenter的实例上同步。

答案 1 :(得分:0)

看起来你没有理由懒洋洋地创建JobInstanceControlCenter单例。

只做

private static final JobInstanceControlCenter _instance = new JobInstanceControlCenter();

然后您不必担心同步的getter或尝试双重检查锁定。除非你故意试图推迟加载,否则这是简单和无错误的单例模式。