Непятничное;-) thread-safe servlets & Servlet 2.4 vs. Servlet 3.0
Имея траблы с многопоточностью в веб приложении решил проанализировать задачу на простейшем примере. Для этого я взял "сложнейший";-))) сорц примера (обычного счетчика, объявленного private ) не thread-safe сервлета с из этой древней статьи Write thread-safe servlets и хтмл-код оттуда же (в котором череи ифрейм стартуют сервлеты три раза). Однако следющая фигня обнаружена была:
Если я декларирую параметры в web.xml (servlet-name,url-pattern....) то после старта сервлета и вызова хтмл-страницы...счетчик во фреймах увеличивается правильно почему то, хотя в ява-коде нет синхронизации то
frame1
com.threadtest.ThrdTstServlet1@367c3:
Counter Test= 0
Counter Test=1
Counter Test= 2
Counter Test= 3
Counter Test= 4
Counter Test=5
Counter Test= 6
Counter Test= 7
Counter Test= 8
Counter Test= 9
frame2
com.threadtest.ThrdTstServlet1@367c3:
Counter Test= 10
Counter Test=11
Counter Test= 12
Counter Test= 13
Counter Test= 14
Counter Test=15
Counter Test= 16
Counter Test= 17
Counter Test= 18
Counter Test=19
frame3
com.threadtest.ThrdTstServlet1@367c3:
Counter Test= 20
Counter Test=21
Counter Test= 22
Counter Test= 23
Counter Test= 24
Counter Test=25
Counter Test= 26
Counter Test= 27
Counter Test= 28
Counter Test=29
Если же я закоменнтирую параметры в display-name, servlet-mapping и оставлю только "шапку" , где версия объявлена и welcome-file-list. А в ява-коде пропишу @WebServlet("/tsttrd1"), то несинхронизированность проявит себя (как и описано было в статье)
frame1
com.threadtest.ThrdTstServlet1@11a67b9:
Counter Test= 0
Counter Test=2
Counter Test= 4
Counter Test= 6
Counter Test= 8
Counter Test=10
Counter Test= 12
Counter Test= 14
Counter Test= 17
Counter Test=19
frame2
com.threadtest.ThrdTstServlet1@11a67b9:
Counter Test= 0
Counter Test=2
Counter Test= 3
Counter Test= 5
Counter Test= 7
Counter Test=9
Counter Test= 11
Counter Test= 13
Counter Test= 15
Counter Test=17
frame3
com.threadtest.ThrdTstServlet1@11a67b9:
Counter Test= 20
Counter Test=21
Counter Test= 22
Counter Test= 23
Counter Test= 24
Counter Test=25
Counter Test= 26
Counter Test= 27
Counter Test= 28
Counter Test=29
Почему так????
P.S под катами коды
package com.threadtest;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
//import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class ThrdTstServlet
*
http://www.javaworld.com/javaworld/jw-07-2004/jw-0712-threadsafe.html?page=3
We create simultaneous requests by using HTML frames; each frame's source is the same servlet: see {@WEB_default.html file with calls of this servlet.
*/
//@WebServlet("/tsttrd1")
public class ThrdTstServlet1 extends HttpServlet {
private static final long serialVersionUID = 1L;
//A variable that is NOT thread-safe!
private int counter = 0;
// private String mutex = "";// mutual exclusion
/**
* @see HttpServlet#HttpServlet()
*/
public ThrdTstServlet1() {
super();
System.out.println("ThrdTstServlet constructor");
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
System.out.println("ThrdTstServlet init(ServletConfig config) called");
}
@Override
public void init() throws ServletException {
super.init();
System.out.println("ThrdTstServlet init() called");
}
/**
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
*/
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// super.service(request, response);
System.out.println("ThrdTstServlet service(HttpServletRequest request, HttpServletResponse response) called");
// synchronized (mutex) {
//A variable that IS thread-safe!
// int counter = 0;
response.getWriter().println("");
response.getWriter().println(this + ":
");
for (int c = 0; c < 10; c++) {
response.getWriter().println("Counter Test= " + counter + "
");
// System.out.println("Counter Test= " + counter);
try {
Thread.currentThread().sleep((long) ( Math.random() * 1000) );
counter++;
} catch (InterruptedException exc) {
}
}
response.getWriter().println("");
// }
}
}
...
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
AtestThreadSafe
ThrdTstServlet1
ThrdTstServlet1
com.threadtest.ThrdTstServlet1
ThrdTstServlet1
/tsttrd1
index.html
index.htm
index.jsp
default.html
default.htm
default.jsp
<HTML>
<BODY>
<TABLE>
<TR>
<TD>
<IFRAME src="/AtestThreadSafe/tsttrd1"
name="servlet1"
height="100%">
</IFRAME>
</TD>
</TR>
<TR>
<TD>
<IFRAME src="/AtestThreadSafe/tsttrd1"
name="servlet2"
height="100%">
</IFRAME>
</TD>
</TR>
<TR>
<TD>
<IFRAME src="/AtestThreadSafe/tsttrd1"
name="servlet3"
height="200%">
</IFRAME>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>
Upd.0
Уточнения про индокод и браузеры:
Во-первых, в изначальную копипасту пробралась ашибко (arteamon заметил): "Кстати, что за адская жесть
Thread.sleep((long) Math.random() * 1000);Math.random() приведенный к long будет же всегда 0. Индусский прогрев процессора?"
Сей фейл пофиксен скобками Thread.sleep((long) ( Math.random() * 1000) );
И вот что обнаружилось после фикса: появились задержки вызовов сервлета.
Если я вызываю страницу с ифреймами в ИЕ 8м (или движке его из под среды разработки екслипс) - то все как и положено: счетчики не синхронные.
Однако, если я стартую ту же страницу с ифреймами в файрефоксе или хроме - то счетчики синхронные. Использовал томкат 7.0.28 +windows (xp / 7).
Если я декларирую параметры в web.xml (servlet-name,url-pattern....) то после старта сервлета и вызова хтмл-страницы...счетчик во фреймах увеличивается правильно почему то, хотя в ява-коде нет синхронизации то
frame1
com.threadtest.ThrdTstServlet1@367c3:
Counter Test= 0
Counter Test=1
Counter Test= 2
Counter Test= 3
Counter Test= 4
Counter Test=5
Counter Test= 6
Counter Test= 7
Counter Test= 8
Counter Test= 9
frame2
com.threadtest.ThrdTstServlet1@367c3:
Counter Test= 10
Counter Test=11
Counter Test= 12
Counter Test= 13
Counter Test= 14
Counter Test=15
Counter Test= 16
Counter Test= 17
Counter Test= 18
Counter Test=19
frame3
com.threadtest.ThrdTstServlet1@367c3:
Counter Test= 20
Counter Test=21
Counter Test= 22
Counter Test= 23
Counter Test= 24
Counter Test=25
Counter Test= 26
Counter Test= 27
Counter Test= 28
Counter Test=29
Если же я закоменнтирую параметры в display-name, servlet-mapping и оставлю только "шапку" , где версия объявлена и welcome-file-list. А в ява-коде пропишу @WebServlet("/tsttrd1"), то несинхронизированность проявит себя (как и описано было в статье)
frame1
com.threadtest.ThrdTstServlet1@11a67b9:
Counter Test= 0
Counter Test=2
Counter Test= 4
Counter Test= 6
Counter Test= 8
Counter Test=10
Counter Test= 12
Counter Test= 14
Counter Test= 17
Counter Test=19
frame2
com.threadtest.ThrdTstServlet1@11a67b9:
Counter Test= 0
Counter Test=2
Counter Test= 3
Counter Test= 5
Counter Test= 7
Counter Test=9
Counter Test= 11
Counter Test= 13
Counter Test= 15
Counter Test=17
frame3
com.threadtest.ThrdTstServlet1@11a67b9:
Counter Test= 20
Counter Test=21
Counter Test= 22
Counter Test= 23
Counter Test= 24
Counter Test=25
Counter Test= 26
Counter Test= 27
Counter Test= 28
Counter Test=29
Почему так????
P.S под катами коды
package com.threadtest;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
//import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class ThrdTstServlet
*
http://www.javaworld.com/javaworld/jw-07-2004/jw-0712-threadsafe.html?page=3
We create simultaneous requests by using HTML frames; each frame's source is the same servlet: see {@WEB_default.html file with calls of this servlet.
*/
//@WebServlet("/tsttrd1")
public class ThrdTstServlet1 extends HttpServlet {
private static final long serialVersionUID = 1L;
//A variable that is NOT thread-safe!
private int counter = 0;
// private String mutex = "";// mutual exclusion
/**
* @see HttpServlet#HttpServlet()
*/
public ThrdTstServlet1() {
super();
System.out.println("ThrdTstServlet constructor");
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
System.out.println("ThrdTstServlet init(ServletConfig config) called");
}
@Override
public void init() throws ServletException {
super.init();
System.out.println("ThrdTstServlet init() called");
}
/**
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
*/
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// super.service(request, response);
System.out.println("ThrdTstServlet service(HttpServletRequest request, HttpServletResponse response) called");
// synchronized (mutex) {
//A variable that IS thread-safe!
// int counter = 0;
response.getWriter().println("");
response.getWriter().println(this + ":
");
for (int c = 0; c < 10; c++) {
response.getWriter().println("Counter Test= " + counter + "
");
// System.out.println("Counter Test= " + counter);
try {
Thread.currentThread().sleep((long) ( Math.random() * 1000) );
counter++;
} catch (InterruptedException exc) {
}
}
response.getWriter().println("");
// }
}
}
...
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<HTML>
<BODY>
<TABLE>
<TR>
<TD>
<IFRAME src="/AtestThreadSafe/tsttrd1"
name="servlet1"
height="100%">
</IFRAME>
</TD>
</TR>
<TR>
<TD>
<IFRAME src="/AtestThreadSafe/tsttrd1"
name="servlet2"
height="100%">
</IFRAME>
</TD>
</TR>
<TR>
<TD>
<IFRAME src="/AtestThreadSafe/tsttrd1"
name="servlet3"
height="200%">
</IFRAME>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>
Upd.0
Уточнения про индокод и браузеры:
Во-первых, в изначальную копипасту пробралась ашибко (arteamon заметил): "Кстати, что за адская жесть
Thread.sleep((long) Math.random() * 1000);Math.random() приведенный к long будет же всегда 0. Индусский прогрев процессора?"
Сей фейл пофиксен скобками Thread.sleep((long) ( Math.random() * 1000) );
И вот что обнаружилось после фикса: появились задержки вызовов сервлета.
Если я вызываю страницу с ифреймами в ИЕ 8м (или движке его из под среды разработки екслипс) - то все как и положено: счетчики не синхронные.
Однако, если я стартую ту же страницу с ифреймами в файрефоксе или хроме - то счетчики синхронные. Использовал томкат 7.0.28 +windows (xp / 7).
