Как заставить создать один поток с помощью scheduledexecutorservice и избежать многопоточности

Я хотел бы выполнить следующий код JAVA только один раз при входе на страницу welcome.jsp.

welcome.jsp:

<%WeeklyScheduledMail wsm = WeeklyScheduledMail.INSTANCE;
wsm.startThread(); %>

Таким образом, если пользователь получает доступ к веб-сайту после запуска сервера, этот код можно использовать один раз, а другие пользователи, которые входят в систему и получают доступ к welcome.jsp, не будут выполнять этот код JAVA.

Сначала я попытался реализовать паттерн Синглтон с помощью enum, я думал, что этого будет достаточно, но это не сработало. Я также пробовал использовать ключевое слово Synchronized для методов, но ничего ...

Я уверен, что сделал что-то не так или есть лучший способ делать то, что я хочу делать.

Некоторые части кода:

WeeklyScheduledMail.java:

public enum WeeklyScheduledMail{

    INSTANCE;

    public void startThread() {

        ScheduledExecutorService scheduler = 
        Executors.newSingleThreadScheduledExecutor();

        Runnable task = new TaskSendEmail();
        int initialDelay = 0;
        int periodicDelay = 10;
        scheduler.scheduleAtFixedRate(task, initialDelay, periodicDelay, 
        TimeUnit.SECONDS);          

    }
}

TaskSendEmail.java:

public class TaskSendEmail implements Runnable{

    public void run() {
        System.out.println("Hello: "+System.currentTimeMillis());
    }
}

Попробуйте событие Webapplication, то есть contextInitialized(), оно запустится только один раз.

user2575725 02.05.2018 09:21

Не кажется хорошей идеей управлять такими функциями из JSP, это следует делать при запуске приложения.

daniu 02.05.2018 09:22

@daniu Я тоже так думаю, но мне нужно «активировать» этот Java-код, когда какой-то пользователь с определенной ролью (администратор) получает доступ к странице welcome.jsp.

Cheknov 02.05.2018 09:24

@Arvind Интересно! Не могли бы вы немного проиллюстрировать процесс? Это не обязательно должно быть в моем коде, просто более развернутый пример, чтобы понять, как его реализовать.

Cheknov 02.05.2018 09:26

@Gera, пожалуйста, обратитесь к сообщению stackoverflow.com/a/50129252/2575725 на самом деле делает то же самое

user2575725 02.05.2018 09:52
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
5
336
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы столкнулись с неправильной концепцией запуска кода инициализации приложения. JSP и другие ресурсы, доступные пользователю, не предназначены для этого. Даже если вы можете использовать некоторую ленивую логику инициализации, накладных расходов все равно можно будет избежать.

То, что вы ищете, предоставляется JavaEE: слушатель контекста, который вызывается один раз при запуске приложения, чтобы уведомить ваше приложение о том, что контекст был инициализирован:

public class MyContextListener implements javax.servlet.ServletContextListener {

    private static fWeeklyScheduledMail wsm = 
            weeklyScheduledMail.INSTANCE;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
            wsm.startThread();
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        wsm.stopThread();
    }
}

Затем этот слушатель должен быть зарегистрирован в дескрипторе развертывания web.xml (под web-app):

<listener>
     <listener-class>my.packg.MyContextListener</listener-class>
</listener>

Приведенный выше код и конфигурация заставят ваше расписание запускаться один раз при запуске приложения.

Оно работает! Большое спасибо. Но я хотел бы знать, можно ли выполнять аналогичные задачи (один поток или несколько потоков), если произойдет какое-то конкретное событие. Слушатель инициализирует поток при запуске сервера, но ... что, если я хочу, чтобы поток был инициализирован после того, как в веб-приложении произойдет определенное событие?

Cheknov 02.05.2018 10:46

@Gera Какого типа другие мероприятия?

ernest_k 02.05.2018 10:47

т.е. когда пользователь с ролью администратора нажимает кнопку внутри определенной страницы.

Cheknov 02.05.2018 10:49

@Gera Нет, для этого можно использовать "события". Для этого следует использовать обычный код приложения (сервлет `doXyz1, прослушиватель JSF и т. д.). У вас также есть прослушиватели событий для пользовательских сеансов, но ни один прослушиватель не предназначен для обработки взаимодействия с пользователем.

ernest_k 02.05.2018 10:51

Другие вопросы по теме