package count;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleCounter extends HttpServlet{

	int count = 0;
	
	public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		res.setContentType("text/plain");
		PrintWriter out = res.getWriter();
		
		++count;
		
		
		out.println("Since loading, this servlet has been accessed " + count + "times.");
	}
}



서블릿 개발자 입장에서 각각의 클라이언트는 service(), doGet(), doPost() 메서드를 통해 서블릿을 호출하는
다른 쓰레드이다. 만일 서블릿이 단순히 요청을 읽고 응답을 리턴하고 지역변수에 저장하기만 한다면,
이들 쓰레드간 상호 연결에 대해서는 걱정할 필요가 없다.

그러나, 일단 어떠한 정보든지 비지역 변수에 저장되면 각각의 클라이언트 쓰레드가 서블릿의 비지역 변수를
조작할 수 있는 능력을 가지게 된다.

만약 두 개의 요청이 동시에 SimpleCounter에 들어온다면, count에 대해 동일한 값을 출력할 가능성이 있다.한 쓰레드가 일단 카운트 값을 증가시키고, 첫번째 쓰레드가 값을 출력하기 전에 뒤이어 두번째 쓰레드가 카운트 값을 증가시켰다면 각각의 쓰레드는 값을 2 만큼 증가시킨 동일한 값을 출력하게 된다.

이러한 유형의 문제를 막기 위해 코드에 하나 이상의 동기화 블록을 처 줘야 한다.

동기화는 필요할 때만 사용되어야 한다. 이유는 몇몇 플랫폼에서 동기화된 블록을 매번 들어갈 때 마다 모니터를
얻기 위해 많은 오버헤드가 필요하기 때문이다. 또 하나 중요한 것은 하나의 쓰레드가 동기화된 코드를 수행하는
동안 다른 쓰레드는 모니터가 해제되길 기다리면서 블로킹이 될 수 있다는 것이다.


좋은 글이면 추천 눌러 주세요! 힘이 됩니다 ^^








동기화에 대한 방법 3가지를 소스로 보이겠다.


첫번째로, doGet()에 synchronized 키워드를 붙이는 것이다. 이 방법은 서블릿이 한 번에 하나의 GET 요청만 다룰 수 있기 때문에 적절한 방법은 아니다.
package count;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleCounter extends HttpServlet{

	int count = 0;
	
	public synchronized void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		res.setContentType("text/plain");
		PrintWriter out = res.getWriter();
		
		++count;
		
		
		out.println("Since loading, this servlet has been accessed " + count + "times.");
	}
}
두번째는 단계적으로 수행되는 두 행을 동기화시키는 것이다. 이 방법은 동기화된 블록에서 서블릿이 소요되는 시간을 제한하지만, 한편으로는 일관성 있는 카운트라는 동일한 목적을 수행하기 때문에 잘 동작한다.
package count;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleCounter extends HttpServlet{

	int count = 0;
	
	public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		res.setContentType("text/plain");
		PrintWriter out = res.getWriter();
		
                synchronized(this) {

		++count;
		out.println("Since loading, this servlet has been accessed " + count + "times.");
                }
	}
}
세번째는 순차적으로 수행되어야 할 필요가 있는 모든 작업을 동기화 블록 안에 넣고, 동기화된 블록 밖에서는 그 결과를 사용하는 것이다. 이러한 수정은 동기화된 블록을 가능한 한 작게 만들고 일관성 있게 카운트를 유지한다.
package count;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleCounter extends HttpServlet{

	int count = 0;
	
	public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		res.setContentType("text/plain");
		PrintWriter out = res.getWriter();
		
                int local_count;
                synchronized(this) {
		        local_count= ++count;
		}
		out.println("Since loading, this servlet has been accessed " + local_count + "times.");
	}
}
posted by 쪼재