Сервлеты и синхронизация - PullRequest
2 голосов
/ 18 марта 2011

Сервлеты запускаются в нескольких потоках, поэтому мой вопрос:

Если у меня много сервлетов, которые вызывают какой-то служебный класс (DbUtils, например

Connection c = DbUtils.getConnection();
//....some action with db here

, я должен принять дополнительныедействия для синхронизации внутри DbUtils?

На самом деле я хотел бы наследовать HttpServlet в нечто вроде DatabaseInvokerServlet:

public abstract class DatabaseInvokerServlet extends HttpServlet

с методами:

public abstract void getResultSets(Connection connection) throws SQLException;
private AbstractUser currentUser;
private HttpServletRequest request;
private HttpServletResponse response;
protected void processData() {}
protected void afterRequestProcessed() throws ServletException, IOException {}
protected void beforeRequestProcessed() throws ServletException, IOException {}

protected void execute() {
    Connection c = null;
    try {
        c = DbUtils.getConnection();
        getResultSets(c);
        processData();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if (c != null) {
                c.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

public HttpServletRequest getRequest() {
    return request;
}

public HttpServletResponse getResponse() {
    return response;
}

public AbstractUser getCurrentUser() {
    return currentUser;
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");
    response.setCharacterEncoding("UTF-8");


    this.request = request;
    this.response = response;
    this.currentUser = (AbstractUser) request.getSession().getAttribute("currentUser");

}

Тогда я бы просто унаследовал свой DatabaseInvokerServlet для новых сервлетов, чтобы выполнять пользовательские операции. Причина не в том, чтобы копировать-вставлять блок вызова базы данных с try-catch-finally во многих местах.

Но, как яЯ вижу, что такой подход не будет работать из-за проблем с синхронизацией. Я прав?

Ответы [ 4 ]

3 голосов
/ 18 марта 2011

Если DbUtils создает соединение в том же потоке, например:

public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(url, username, password);
}

, тогда это потокобезопасно.

Но если соединение является переменной класса, например:

private static Connection connection = DriverManager.getConnection(url, username, password);

public static Connection getConnection() throws SQLException {
    return connection;
}

Тогда оно определенно не является потокобезопасным, поскольку одно и то же соединение будет общим для всех потоков.Также, когда оно закрыто в потоке, все последующие потоки не смогут использовать соединение, потому что оно больше не открыто.Кроме того, когда он никогда не закрывается, БД прервет соединение раньше или позже, обычно через несколько часов, и ваше приложение больше не будет работать, потому что соединение больше не открыто.


Что касаетсясервлет,

public abstract class DatabaseInvokerServlet extends HttpServlet {
    private AbstractUser currentUser;
    private HttpServletRequest request;
    private HttpServletResponse response;
    // ...
}

это определенно не потокобезопасен.Вы назначаете текущего пользователя, запрос и ответ как переменные экземпляра.Из каждого класса сервлета существует только один экземпляр за время существования приложения.Этот экземпляр является общим для всех посетителей / сеансов в течение всего срока службы приложения.Каждый HTTP-запрос работает в отдельном потоке и использует один и тот же экземпляр.

Представьте себе двух одновременных посетителей: посетитель А задает текущего пользователя, запрос и ответ.Процесс БД, однако, занимает много времени.Перед возвратом ответа посетителя A посетитель B вызывает тот же сервлет, и поэтому текущий пользователь, запрос и ответ будут переопределены.Затем запрос посетителя A завершается и он хочет написать ответ, вместо этого он пишет в ответ посетителя B!Посетитель B видит результат запроса посетителя A, а посетитель A ничего не видит на своем экране!

Никогда не следует назначать данные, относящиеся к запросу / сеансу, в качестве переменной экземпляра сервлета.Вы должны сохранять их метод (поток) локальным.

public abstract class DatabaseInvokerServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        AbstractUser currentUser = request.getSession().getAttribute("user");
        // Keep the variables in the method block! 
        // Do not assign them as instance variable!
    }
}

Что касается полной картины, этот подход является неуклюжим.Уровень доступа к базе данных не должен иметь ничего общего с сервлетами.Он должен работать в своих собственных автономных классах, которые вы можете просто создать / вызвать в любом другом Java-классе, любом классе сервлета или обычном приложении с main() или чем-то еще.У вас не должно быть ни одной строки java.sql.* импорта в ваших классах сервлетов (ожидайте, возможно, SQLException, если он не абстрагирован).У вас не должно быть ни одной строки javax.servlet.* импорта в ваших классах базы данных.

См. Также:

1 голос
/ 18 марта 2011

Если я угадаю правильно, DBUtils возвращает новый экземпляр для каждого вызова getConnection (). А поскольку класс DBUtils является служебным классом, он не должен поддерживать никакого состояния. В этом случае нет необходимости в дополнительных усилиях для синхронизации.

1 голос
/ 18 марта 2011

Сервлеты запускаются в нескольких потоках.

The J2EE spec says there is only one instance per servlet class running in one web container for non single thread servlet. 

Спецификации сервлета 2.3

Контейнер сервлета может отправлять параллельные запросы через метод обслуживания сервлета.Для обработки запросов разработчик сервлета должен предоставить адекватные условия для параллельной обработки с несколькими потоками в методе обслуживания.

Синхронизация в сервлете.

Never have an member variable in a servlet, it is not thread safe.
1 голос
/ 18 марта 2011

Если служебный класс имеет состояние (пример: переменные класса или экземпляра), скорее всего, да.

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