Singleton EJB Concurrency Management и служба EJB Timer - выполнение методов чтения-записи - PullRequest
0 голосов
/ 22 февраля 2019

Ниже приведен мой Singleton EJB, который использует управляемый компонентом Bean:

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 - Пример сессионного компонента Singleton

Управляемый компонентом параллелизм

Синглтоны, использующие управляемый компонентом параллелизм, обеспечивают полный параллельный доступ ко всем бизнес-методам и методам тайм-аута в синглтоне.Разработчик синглтона отвечает за синхронизацию состояния синглтона между всеми клиентами.Разработчикам, которые создают синглтоны с управляемым компонентом параллелизмом, разрешается использовать примитивы синхронизации языка программирования Java, такие как синхронизация и volatile, для предотвращения ошибок при одновременном доступе.

Почему это не ведет себя таким образомчто я ожидаю, что это будет;у меня есть какие-то недостатки в моем дизайне?

Изначально для моего 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...