Singleton EJB并发管理和EJB计时器服务-读写方法的执行

时间:2019-02-22 10:15:01

标签: concurrency timer singleton ejb ejb-3.1

以下是我的使用Bean托管并发的Singleton EJB:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.annotation.PostConstruct;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.Singleton;
import javax.ejb.Startup;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Startup
@Singleton(mappedName = "MySingletonService")
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class MySingletonService {

    private final Logger logger = LogManager.getLogger(getClass());
    private final AtomicBoolean running = new AtomicBoolean(false);
    private final Lock lock = new ReentrantLock();

    @PostConstruct
    public void initialize() {
        try {
            initializeInternal();
        } catch (Exception cause) {
            logger.debug(cause.getMessage(), cause);
        }
    }

    public boolean isRunning() {
        logger.debug("Is initialization running?: {}", running.get());
        return running.get();
    }

    public void reInitialize() throws InterruptedException {
        initializeInternal();
    }

    private void initializeInternal() throws InterruptedException {
        if (lock.tryLock(5, TimeUnit.MINUTES)) {
            try {
                setRunning(true);
                logger.debug("Initialization running");

                try {
                    Thread.sleep(TimeUnit.SECONDS.toMillis(30));
                } catch (Exception cause) {
                }
            } finally {
                setRunning(false);
                lock.unlock();
            }
        }
    }

    private void setRunning(boolean state) {
        running.set(state);
    }
}

以下是我的调度程序:

import java.util.Objects;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.ScheduleExpression;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Startup
@Singleton(mappedName = "MySchedulerService")
public class MySchedulerService {

    private final Logger logger = LogManager.getLogger(getClass());

    @Resource
    private TimerService timerService;

    @EJB(mappedName = "MySingletonService")
    private MySingletonService mySingletonService;

    @PostConstruct
    public void initialize() {
        try {
            ScheduleExpression schedule = new ScheduleExpression()
                .second("*/10")
                .minute("*")
                .hour("*")
                .dayOfMonth("*")
                .month("*")
                .dayOfWeek("*")
                .year("*");
            TimerConfig timerConfig = new TimerConfig("MySingletonServiceTimer", false);            
            timerService.createCalendarTimer(schedule, timerConfig);            
        } catch (Exception cause) {
            logger.error(cause.getMessage(), cause);
        }            
    }

    @Timeout
    public void scheduledTask(Timer timer) {
        logger.debug("Executing scheduler");

        if(StringUtils.equals("MySingletonServiceTimer", Objects.toString(timer.getInfo(), null))) {
            if(mySingletonService.isRunning()) {
                return;
            }

            try {
                mySingletonService.reInitialize();
            } catch (Exception cause) {
                logger.error(cause.getMessage(), cause);
            } 
        }
    }
}

我希望isRunning()执行某些任务时执行initializeInternal()方法(在上面的代码中它正在休眠)。

scheuledTask每10秒执行一次,而initializeInternal()内有30秒的睡眠时间。我希望看到isRunning()中定义的消息在两次initializeInternal()的连续调用中至少被记录两次。但实际上并非如此。当initializeInternal()正在累积lock时,isRunning()也进入等待模式。

根据Java EE Tutorial - Singleton Session Bean Example

  

Bean管理的并发性

     

使用Bean管理的并发的单例允许完全并发   访问单例中的所有业务和超时方法。的   单身人士的开发商负责确保国家   单例的同步在所有客户端上进行。开发者   使用Bean管理的并发创建单例可以使用   Java编程语言同步原语,例如   同步和易失性,以防止在并发期间出​​错   访问。

为什么它的行为方式不符合我的预期;我的设计有什么缺陷吗?

最初,我为MySingletonService定义了容器管理的并发性:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.annotation.PostConstruct;
import javax.ejb.AccessTimeout;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;
import javax.ejb.Startup;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Startup
@Singleton(mappedName = "MySingletonService")
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class MySingletonService {

    private final Logger logger = LogManager.getLogger(getClass());
    private final AtomicBoolean running = new AtomicBoolean(false);

    @PostConstruct
    public void initialize() {
        try {
            initializeInternal();
        } catch (Exception cause) {
            logger.debug(cause.getMessage(), cause);
        } 
    }

    @Lock(LockType.READ)
    public boolean isRunning() {
        logger.debug("Is initialization running?: {}", running.get());
        return running.get();
    }

    @Lock(LockType.WRITE)
    @AccessTimeout(value = 5, unit = TimeUnit.MINUTES)
    public void reInitialize() {        
        initializeInternal();
    }

    private void initializeInternal() {
        try {
            setRunning(true);               
            logger.debug("Initialization running");

            try {
                Thread.sleep(TimeUnit.SECONDS.toMillis(30));
            } catch (Exception cause) {                 
            }       
        } finally {
            setRunning(false);
        }
    }

    private void setRunning(boolean state) {
        running.set(state);
    }
}

这也表现出相同的方式,所以我认为使用Bean管理并发可以使我获得想要的成就。

以下是针对两种托管并发类型生成的日志,这可能解释了执行流程:

2019-02-22 09:58:37,047 : DEBUG : main : MySingletonService : initializeInternal : Initialization running
2019-02-22 09:59:10,037 : DEBUG : EjbTimerPool - 1 : MySchedulerService : scheduledTask : Executing scheduler
2019-02-22 09:59:10,044 : DEBUG : EjbTimerPool - 1 : MySingletonService : isRunning : Is initialization running?: false
2019-02-22 09:59:10,051 : DEBUG : EjbTimerPool - 1 : MySingletonService : initializeInternal : Initialization running
2019-02-22 09:59:40,052 : DEBUG : EjbTimerPool - 2 : MySchedulerService : scheduledTask : Executing scheduler
2019-02-22 09:59:40,053 : DEBUG : EjbTimerPool - 2 : MySingletonService : isRunning : Is initialization running?: false
2019-02-22 09:59:40,053 : DEBUG : EjbTimerPool - 2 : MySingletonService : initializeInternal : Initialization running
2019-02-22 10:00:10,055 : DEBUG : EjbTimerPool - 1 : MySchedulerService : scheduledTask : Executing scheduler
2019-02-22 10:00:10,055 : DEBUG : EjbTimerPool - 1 : MySingletonService : isRunning : Is initialization running?: false
2019-02-22 10:00:10,056 : DEBUG : EjbTimerPool - 1 : MySingletonService : initializeInternal : Initialization running

注意:我正在将TomEE 7.0.4与OpenJDK 8u192一起使用。

0 个答案:

没有答案