개발관련/자바2010. 6. 10. 11:35
아래 내용은 채소좋아라는 분이 쓰신 글입니다.

----------------------------------------------------------------------------------------------------

태초에(Java 1.0 ~ 1.1)는

Java 언어에서 제공하는 key, value 쌍을 지원하는 데이터 Container는

Hashtable 클래스가 제공되었었습니다.

그런데 이 Hashtable 은 느리기도 하고 지원하는 메소드도 기능이 많이 부족해

 

Java 1.2 부터는 HashMap 를 포함해 여러 key,value 쌍을 지원하는 클래스가 새로 생겼는데요.

이와 동시에 이런 key, value 쌍을 쓰는 클래스들의 공통된 인터페이스를 Map 인터페이스로 묶었습니다.

(활용성을 높이기 위해)

Hashtable 도 Java 1.2 부터 Map을 implements 하게 시작합니다.

 

그래서

key,value 쌍을 지원하는 대부분의 클래스들은 Map 을 구현하게 되어있는데

대표적인 Hashtable, HashMap 은 key 와 value 를 hash 알고리즘에 의해 구현해 놓은 것입니다.

TreeMap 같은 경우는 이름에서부터 알수 있듯이 tree 알고리즘에 의해 key, value를 저장하고 있습니다.

구현체마다 독특한 특성이 있는데 HashMap의 경우 hash알고리즘에 의해 get이 매우 빠르며

TreeMap 의 경우 tree 소팅을 한상태로 저장함으로 data 소팅시 유리합니다.

 

그럼 같은 알고리즘을 쓰는 Hashtable과 HashMap 은 무슨 차이가 있냐면

Hashtable 의 모든 Data 변경 매소드는 syncronized 로 선언되어있습니다.

즉 매소드 호출 전 쓰레드간 동기화 락을 통해 멀티 쓰레드 환경에서 data의 무결성을 보장해줍니다.

반대로 HashMap 은 그런 선언이 없기 때문에 멀티 쓰레드에서 여러 쓰레드가 동시에 객체의

data 를 조작하는 경우 data가 깨져버리고 심각한 오류가 발생할 수 있습니다.

다만 이 동기화 락이 매우 느린 동작이기때문에 Hashtable 보다 HashMap 이 훨씬 빠릅니다.

Map 객체가 단일 쓰레드에서만 쓰일때는 HashMap을 사용해야합니다.

 

프로그래밍상의 편의성 때문에 멀티쓰레드 환경에서도 Hashtable 을 쓰기 보다는

HashMap을 다시 감싸서

 

Map m = Collections.synchronizedMap(new HashMap(...));


과 같은 형태가 최근에는 더 선호됩니다.

자세한 사용 설명은 javadoc 문서를 참고하세요.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/HashMap.html

 

Java 1.5 환경부터는 cuncurrent util 이 default 로 제공되게 되어

멀티 쓰레드 환경에서 위 방법 보다는 CuncurrentHashMap 클래스를 사용하는게 더 선호됩니다.

CuncurrentHashMap 은 synchronizedMap 으로 감싸진 HashMap 이나 Hashtable 보다 더 빠르면서도 쓰레드간 동기화를 보장해 주는데

이는 동기화시 hashtable을 전체에 대해 lock을 걸지 않고 조각을 쪼개어 부분부분 lock 걸어

쓰레드간 경쟁이 심할시에 이득을 보는 구현입니다.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html

 

참고하시고

Java 라는 언어는 계속 발전하고 있음으로

새로운 버전이 나올때 새로운 기능에 관심을 갖을 필요가 있습니다.

'개발관련 > 자바' 카테고리의 다른 글

java의 for-each 구문 확장  (0) 2010.06.10
[펌]템플릿 형태 JSP  (0) 2010.01.06
[펌][JAVA] static키워드 바로알기  (0) 2009.12.18
[펌]GlassFish와 Tomcat 비교  (0) 2009.06.12
Java 5.x 이상에서 유용한 기능(generic)  (0) 2009.02.27
Posted by 자개비
개발관련/자바2010. 6. 10. 10:45

출처 : http://leepoint.net/notes-java/flow/loops/foreach.html

for each loop는 java 5.0(1.5)에서 확장된 개념이다. 아래에 이전 버전과 호환성 코드를 참고보면 이해가 쉽다.

For-each Loop

Purpose

The basic for loop was extended in Java 5 to make iteration over arrays and other collections more convenient. This newer for statement is called the enhanced for or for-each (because it is called this in other programming languages). I've also heard it called the for-in loop.

Use it in preference to the standard for loop if applicable (see last section below) because it's much more readable.

Series of values. The for-each loop is used to access each successive value in a collection of values.

Arrays and Collections. It's commonly used to iterate over an array or a Collections class (eg, ArrayList).

Iterable<E>. It can also iterate over anything that implements the Iterable<E> interface (must define iterator() method). Many of the Collections classes (eg, ArrayList) implement Iterable<E>, which makes the for-each loop very useful. You can also implement Iterable<E> for your own data structures.

General Form

The for-each and equivalent for statements have these forms. The two basic equivalent forms are given, depending one whether it is an array or an Iterable that is being traversed. In both cases an extra variable is required, an index for the array and an iterator for the collection.

For-each loop Equivalent for loop
for (type var : arr) {
    body-of-loop
}
for (int i = 0; i < arr.length; i++) { 
    type var = arr[i];
    body-of-loop
}
for (type var : coll) {
    body-of-loop
}
for (Iterator<type> iter = coll.iterator(); iter.hasNext(); ) {
    type var = iter.next();
    body-of-loop
}

Example - Adding all elements of an array

Here is a loop written as both a for-each loop and a basic for loop.

double[] ar = {1.2, 3.0, 0.8};
int sum = 0;
for (double d : ar) {  // d gets successively each value in ar.
    sum += d;
}

And here is the same loop using the basic for. It requires an extra iteration variable.

double[] ar = {1.2, 3.0, 0.8};
int sum = 0;
for (int i = 0; i < ar.length; i++) {  // i indexes each element successively.
    sum += ar[i];
}

Where the for-each is appropriate

Altho the enhanced for loop can make code much clearer, it can't be used in some common situations.

  • Only access. Elements can not be assigned to, eg, not to increment each element in a collection.
  • Only single structure. It's not possible to traverse two structures at once, eg, to compare two arrays.
  • Only single element. Use only for single element access, eg, not to compare successive elements.
  • Only forward. It's possible to iterate only forward by single steps.
  • At least Java 5. Don't use it if you need compatibility with versions before Java 5.
Posted by 자개비
개발관련/자바2010. 1. 6. 23:07
출처 : http://www.4te.co.kr/560

템플릿 형태로 모듈화 해서 jsp를 사용하기 위해서는 include를 사용하면 된다.
include를 사용하는 형식은 "include 지시자"와 "include 표준 액션"을 사용할 수 있다.

include 지시자를 사용하여 코딩하는 예제는 다음과 같다.

<%@ include file="Header.jsp" %>

include 표준 액션은 다음과 같이 사용한다.

<jsp:include page="Header.jsp"></jsp:include>

지시자를 사용한 include일 경우에는 변환시에, include 표준액션은 실행시에 Header.jsp파일을 실행한다.
만일 Header.jsp 파일에서 동적인 내용을 포함할 페이지로부터 받아야 한다면 다음과 같이 사용할 수 있다.

<jsp:include page="Header.jsp">
    <jsp:param name="subTitle" value="We take the sting out of SOAP."/>
</jsp:include>

Header.jsp

<strong>${param.subTitle}</strong>

변수를 미리 선언해 두고 Header.jsp 파일에서 사용하는 방법이 아닌 param 태그를 사용하여 변수를 넘길 수 있다.

* forward 표준액션

다른 jsp 페이지로 요청을 넘길 수도 있다. 이 때 사용하는 액션이 foward 액션이다.
사용 방법은 아래와 같다.

<jsp:forward page="HandleIt.jsp"></jsp:forward>

forward를 싱행하게 되면 forward 이전에 나온 내용은 버퍼에서 삭제해 준다. 다만 forward 이전에 <% out.flush(); %>를 실행하게 되면 이미 버퍼가 출력된 상태이므로 forward 액션은 실행되지 않는다.

Posted by 자개비
개발관련/자바2009. 12. 18. 16:13
[JAVA] static키워드 바로알기

2007/10/16 11:27
| Posted by 이동한

자바를 한번쯤 공부해본사람이라면 static키워드를 모르지는 않을 것입니다.

하지만, 바르게 알고 있는 사람들은 그리 많지 않습니다.


자바경력자를 면접볼 때 static키워드에 대해서 질문하곤 합니다.


면접관 : static키워드에 대해서 설명해보세요.
응시자 : static키워드를 쓰면, 객체를 생성하지 않고도 변수나 함수를 사용할 수 있습니다.


면접관 : 왜 static키워드를 쓰나요?
응시자 : 객체를 생성하지 않아도 되니까 편리하고 속도도 빠릅니다.


면접관 : 그렇다면 모든 변수와 함수에 static을 붙이는 것이 좋겠네요?
응시자 : 가능한한 static을 붙이는 것이 좋다고 생각합니다.


면접관 : 어떤 경우에 static을 붙일 수 있고, 어떤 경우에 static을 붙일 수 없습니까?
응시자 : ...


면접관 : 만일 당신이 새로운 클래스를 작성한다고 할 때, 어떤 경우에 static키워드를
             사용해야한다고 생각합니까?

응시자 : ...


대부분의 경우 위와 같은 내용으로 문답이 진행됩니다.

사실 응시자의 대답은 다 맞는 얘기입니다. 하지만, static의 핵심적인 개념을 모르기 때문에
어떤 경우에 왜 static을 사용해야하는지는 잘모르는 것 같습니다.


먼저 결론부터 간단히 정리하면 다음과 같습니다.


1.클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통적으로 사용해야하는 것에 static을 붙인다.
 - 인스턴스를 생성하면, 각 인스턴스들은 서로 독립적기 때문에 서로 다른 값을 유지한다.
    경우에 따라서는 각 인스턴스들이 공통적으로 같은 값이 유지되어야 하는 경우 static을
    붙인다.


2. static이 붙은 멤버변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
 - static이 붙은 멤버변수(클래스변수)는 클래스가 메모리에 올라갈때 이미 자동적으로
   생성되기 때문이다.


3. static이 붙은 메서드(함수)에서는 인스턴스 변수를 사용할 수 없다.
 - static이 메서드는 인스턴스 생성 없이 호출가능한 반면, 인스턴스 변수는 인스턴스를
    생성해야만 존재하기 때문에... static이 붙은 메서드(클래스메서드)를 호출할 때
    인스턴스가 생성되어있을수도 그렇지 않을 수도 있어서 static이 붙은 메서드에서
    인스턴스변수의 사용을 허용하지 않는다.
    (반대로, 인스턴스변수나 인스턴스메서드에서는 static이 붙은 멤버들을 사용하는 것이
     언제나 가능하다. 인스턴스변수가 존재한다는 것은 static이 붙은 변수가 이미 메모리에
     존재한다는 것을 의미하기 때문이다.)


4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.
 - 메서드의 작업내용중에서 인스턴스 변수를 필요로 한다면, static을 붙일 수 없다.
    반대로 인스턴스변수를 필요로 하지 않는다면, 가능하면 static을 붙이는 것이 좋다.
    메서드 호출시간이 짧아지기 때문에 효율이 높아진다.
    (static을 안붙인 메서드는 실행시 호출되어야할 메서드를 찾는 과정이 추가적으로
    필요하기 때문에 시간이 더 걸린다.)


5. 클래스 설계시 static의 사용지침
 - 먼저 클래스의 멤버변수중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지
    살펴보고 있으면, static을 붙여준다.
 - 작성한 메서드 중에서 인스턴스 변수를 사용하지 않는 메서드에 대해서 static을
    붙일 것을 고려한다.
 
일반적으로 인스턴스변수와 관련된 작업을 하는 메서드는 인스턴스메서드(static이 안붙은
메서드)이고 static변수(클래스변수)와 관련된 작업을 하는 메서드는 클래스메서드(static이 붙은 메서드)라고 보면 된다.


다음은 static에 대한 자세한 설명과 예제입니다.


static은 객체지향개념을 이해하는 가장 중요한 첫걸음이니 확실히 알아두셔야합니다.


==================================================================
   


 

3.2 클래스변수와 인스턴스변수


클래스변수와 인스턴스변수의 차이를 이해하기 위한 예로 카드 게임에 사용되는 카드를 클래스로 정의해보자.



카드 클래스를 작성하기 위해서는 먼저 카드를 분석해서 속성과 기능을 알아 내야한다. 속성으로는 카드의 무늬, 숫자, 폭, 높이 정도를 생각할 수 있을 것이다.
이 중에서 어떤 속성을 클래스 변수로 선언할 것이며, 또 어떤 속성들을 인스턴스 변수로 선언할 것인지 생각해보자.


class Card {
     String kind ;                         // 카드의 무늬 - 인스턴스 변수
     int number;                         // 카드의 숫자 - 인스턴스 변수
     static int width = 100 ;             // 카드의 폭 - 클래스 변수
     static int height = 250 ;            // 카드의 높이 - 클래스 변수
}


각 Card인스턴스는 자신만의 무늬(kind)와 숫자(number)를 유지하고 있어야 하므로 이들을 인스턴스변수로 선언하였고, 각 카드들의 폭(width)과 높이(height)는 모든 인스턴스가 공통적으로 같은 값을 유지해야하므로 클래스변수로 선언하였다.

만일 카드의 폭을 변경해야할 필요가 있을 때는 모든 카드의 width값을 변경하지 않고, 한 카드의 width값만 변경해도 모든 카드의 width값이 변경되는 셈이다.

[예제6-4] CardTest.java

class CardTest{
      public static void main(String args[]) {
            // 클래스변수(static 변수)는 객체생성없이 '클래스이름.클래스변수'로 직접 사용 가능하다.
            System.out.println("Card.width = " + Card.width);
            System.out.println("Card.height = " + Card.height);

            Card c1 = new Card();
            c1.kind = "Heart";
            c1.number = 7;

            Card c2 = new Card();
            c2.kind = "Spade";
            c2.number = 4;

            System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")" );
            System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")" );             System.out.println("이제 c1의 width와 height를 각각 50, 80으로 변경합니다.");
            c1.width = 50;
            c1.height = 80;

            System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")" );
            System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")" );
      }
}

class Card {
     String kind ;                         // 카드의 무늬 - 인스턴스 변수
     int number;                         // 카드의 숫자 - 인스턴스 변수
     static int width = 100;             // 카드의 폭 - 클래스 변수
     static int height = 250;             // 카드의 높이 - 클래스 변수
}

[실행결과]
Card.width = 100
Card.height = 250
c1은 Heart, 7이며, 크기는 (100, 250)
c2는 Spade, 4이며, 크기는 (100, 250)
이제 c1의 width와 height를 각각 50, 80으로 변경합니다.
c1은 Heart, 7이며, 크기는 (50, 80)
c2는 Spade, 4이며, 크기는 (50, 80)


Card클래스의 클래스변수(static변수)인 width, height 그리고 color는 Card클래스의 인스턴스를 생성하지 않고도 '클래스이름.클래스변수'와 같은 방식으로 사용할 수 있다.
Card인스턴스인 c1과 c2는 클래스 변수인 width와 height를 공유하기 때문에, c1의 width와 height를 변경하면 c2의 width와 height값도 바뀐 것과 같은 결과를 얻는다.
Card.width, c1.width, c2.width는 모두 같은 저장공간을 참조하므로 항상 같은 값을 갖게 된다.
인스턴스 변수는 인스턴스가 생성될 때 마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만, 클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.
[플래시동영상]자료실의 플래시동영상 MemberVar.swf을 꼭 보도록하자.



3.9 클래스메서드(static메서드)와 인스턴스메서드


변수에서 그랬던 것과 같이, 메서드 앞에 static이 붙어 있으면 클래스메서드이고 붙어 있지 않으면 인스턴스메서드이다.
클래스 메서드는 호출방법 역시 클래스변수처럼, 객체를 생성하지 않고도 '클래스이름.메서드이름(매개변수)'와 같은 식으로 호출이 가능하다.
그렇다면 어느 경우에 static을 사용해서 클래스메서드로 정의해야하는 것일까?

클래스는 '데이터(변수)와 데이터에 관련된 메서드의 집합'이라고 할 수 있다. 같은 클래스 내에 있는 메서드와 멤버변수는 아주 밀접한 관계가 있다. 인스턴스메서드는 인스턴스변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스변수를 필요로 하는 메서드이다.
그래서 인스턴스변수와 관계없거나(메서드 내에서 인스턴스변수를 사용하지 않거나), 클래스변수만을 사용하는 메서드들은 클래스메서드로 정의한다.

물론 인스턴스변수를 사용하지 않는다고 해서 반드시 클래스 메서드로 정의해야하는 것은 아니지만, 그렇게 하는 것이 일반적이다.
참고로 Math클래스의 모든 메서드는 클래스메서드임을 알 수 있다. Math클래스에는 인스턴스변수가 하나도 없거니와 Math클래스의 함수들은 작업을 수행하는데 필요한 값들을 모두 매개변수로 받아서 처리 하기 때문이다. 이처럼, 단순히 함수들만의 집합인 경우에는 클래스메서드로 선언한다.

[참고]인스턴스 변수 뿐만 아니라 인스턴스 메서드를 호출하는 경우에도 인스턴스 메서드로 선언되어야 한다. 인스턴스 메서드를 호출하는 것 역시 인스턴스 변수를 간접적으로 사용하는 것이기 때문이다.

[예제6-12] MyMathTest2.java

class MyMath2 {
      long a, b;
     
      // 인스턴스변수 a, b를 이용한 작업을 하므로 매개변수가 필요없다.
      long add() {       return a + b; }
      long subtract() {       return a - b; }
      long multiply() {       return a * b; }
      double divide() {       return a / b; }

      // 인스턴스변수와 관계없이 매개변수만으로 작업이 가능하다.
      static long add(long a, long b) {       return a + b; }
      static long subtract(long a, long b) {       return a - b; }
      static long multiply(long a, long b) {       return a * b; }
      static double divide(double a, double b) {       return a / b; }
}

class MyMathTest2 {
      public static void main(String args[]) {
            // 클래스메서드 호출
            System.out.println(MyMath2.add(200L, 100L));
            System.out.println(MyMath2.subtract(200L, 100L));
            System.out.println(MyMath2.multiply(200L, 100L));
            System.out.println(MyMath2.divide(200.0, 100.0));

            MyMath2 mm = new MyMath2();
            mm.a = 200L;
            mm.b = 100L;
            // 인스턴스메서드는 객체생성 후에만 호출이 가능함.
            System.out.println(mm.add());
            System.out.println(mm.subtract());
            System.out.println(mm.multiply());
            System.out.println(mm.divide());

}
[실행결과]
300
100
20000
2.0
300
100
20000
2.0

인스턴스메서드인 add(), subtract(), multiply(), divide()는 인스턴스변수인 a와 b만으로도 충분히 원하는 작업이 가능하기 때문에, 매개변수를 필요로 하지 않으므로 괄호()에 매개변수를 선언하지 않았다.
반면에 add(long a, long b), subtract(long a, long b) 등은 인스턴스변수 없이 매개변수만으로 작업을 수행하기 때문에 static을 붙여서 클래스메서드로 선언하였다. MyMathTest2의 main메서드에서 보면, 클래스메서드는 객체생성없이 바로 호출이 가능했고, 인스턴스메서드는 MyMath2클래스의 인스턴스를 생성한 후에야 호출이 가능했다.
이 예제를 통해서 어떤 경우 인스턴스메서드로, 또는 클래스메서드로 선언해야하는지, 그리고 그 차이를 이해하는 것은 매우 중요하다.



3.10 클래스멤버와 인스턴스멤버간의 참조와 호출.


같은 클래스에 속한 멤버들간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능하다. 단, 클래스멤버가 인스턴스멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.
그 이유는 인스턴스멤버가 존재하는 시점에 클래스멤버는 항상 존재하지만, 클래스멤버가 존재하는 시점에 인스턴스멤버가 항상 존재한다는 것을 보장할 수 없기 때문이다.

[예제6-] ArrayEx.java

class MemberCall {
      int iv = 10;
      static int cv = 20;

      int iv2 = cv;
//   static int cv2 = iv;                   에러. 클래스변수는 인스턴스 변수를 사용할 수 없음.
      static int cv2 = new MemberCall().iv;   // 굳이 사용하려면 이처럼 객체를 생성해야함.

      static void classMethod1() {
            System.out.println(cv);
//         System.out.println(iv);       에러. 클래스메서드에서 인스턴스변수를 바로 사용할 수 없음.
            MemberCall c = new MemberCall();      
            System.out.println(c.iv);   // 객체를 생성한 후에야 인스턴스변수의 참조가 가능함.
     }

      void instanceMethod1() {
            System.out.println(cv);            
            System.out.println(iv);  // 인스턴스메서드에서는 인스턴스변수를 바로 사용가능.
     }

      static void classMethod2() {
            classMethod1();
//         instanceMethod1(); 에러. 클래스메서드에서는 인스턴스메서드를 바로 호출할 수 없음.
            MemberCall c = new MemberCall();
            c.instanceMethod1(); // 인스턴스를 생성한 후에야 인스턴스메서드를 호출할 수 있음.
      }
     
      void instanceMethod2() { // 인스턴스메서드에서는 인스턴스메서드와 클래스메서드
            classMethod1();         // 모두 인스턴스생성없이 바로 호출이 가능하다.
            instanceMethod1();
     }
}

클래스멤버(클래스변수와 클래스메서드)는 언제나 참조 또는 호출이 가능하다.
그렇기 때문에 인스턴스멤버가 클래스멤버를 참조, 호출하는 것은 아무런 문제가 안 된다.
클래스멤버간의 참조 또는 호출 역시 아무런 문제가 없다.

그러나, 인스턴스멤버(인스턴스변수와 인스턴스메서드)는 반드시 객체를 생성한 후에만 참조 또는 호출이 가능하기 때문에 클래스멤버가 인스턴스멤버를 참조, 호출하기 위해서는 객체를 생성하여야 한다.

하지만, 인스턴스멤버간의 호출에는 아무런 문제가 없다. 하나의 인스턴스멤버가 존재한다는 것은 인스턴스가 이미 생성되어있다는 것을 의미하며, 즉 다른 인스턴스멤버들도 모두 존재하기 때문이다.

실제로는 같은 클래스 내에서 클래스멤버가 인스턴스멤버를 참조 또는 호출해야하는 경우는 드물다. 만일 그런 경우가 발생한다면, 인스턴스메서드로 작성해야할 메서드를 클래스메서드로 한 것은 아닌지 한번 더 생각해봐야 한다.

[알아두면 좋아요]

수학에서의 대입법처럼, c = new MemberCall()이므로 c.instanceMethod1();에서 c대신 new MemberCall()을 대입하여 사용할 수 있다.

      MemberCall c = new MemberCall();
      int result = c.instanceMethod1();

위의 두 줄을 다음과 같이 한 줄로 할 수 있다.

      int result = new MemberCall().instanceMethod1();

대신 참조변수를 사용하지 않았기 때문에 생성된 MemeberCall인스턴스는 더 이상 사용할 수 없다

'개발관련 > 자바' 카테고리의 다른 글

java의 for-each 구문 확장  (0) 2010.06.10
[펌]템플릿 형태 JSP  (0) 2010.01.06
[펌]GlassFish와 Tomcat 비교  (0) 2009.06.12
Java 5.x 이상에서 유용한 기능(generic)  (0) 2009.02.27
eclipse DB plugin - DBViewer  (1) 2009.02.27
Posted by 자개비
개발관련/자바2009. 6. 12. 13:36

에너지 효율적 데이터센터 구축을 위한 팁 10가지

애플리케이션의 성능, 사용 용이성, 민첩성을 자세히 비교해 수 있는 방법을 알려 드립니다.



항시 CEO는 IT 관리에 대해 단순한 의문을 품곤 합니다. 왜 고객들은 새로운 온라인 주문 시스템에 불만을 표하는 것일까? 신규 웹 서비스를 출시하는 데 8개월이나 걸리는 이유는 무엇인가? 내가 즐겨 사용하는 재무 보고 애플리케이션이 오늘 아침에 문제를 일으킨 원인은 무엇인가?

하지만 IT 관리 측면에서 '회사가 어떤 애플리케이션 서버 기술을 사용하고 있는가' 에 대해 의문을 던지는 CEO는 거의 찾아보기 힘듭니다. 이는 상당히 중요한 사안이라고 볼 수 있는데, 왜냐하면 개발팀이 사용하는 애플리케이션 서버와 기업의 애플리케이션 성능/민첩성 간에는 직접적인 연관성이 있기 때문입니다.

좀 더 구체적으로 설명하자면, 애플리케이션 서버에서 사용되는 웹 컨테이너 기술은 애플리케이션의 품질과 개발자의 생산성을 결정짓는 중요한 요소입니다. 결국 웹 컨테이너 기술을 올바로 도입한다면 개발팀의 업무 효율이 향상되고 고성능 애플리케이션을 신속하게 개발할 수 있는 효과가 있겠지만, 선택을 잘못 할 경우에는 이 자체가 애물단지로 전락할 공산이 커집니다.

따라서 여러분의 비즈니스에서 애플리케이션이 중추적 역할을 담당하고 있다면 애플리케이션 서버, 웹 컨테이너 기술은 물론, 이들이 개발팀에 어떠한 영향을 미치는지 세심하게 조사해봐야 합니다. 이에 본 기사에서는 현재 널리 애용되고 있는 오픈 소스 옵션인 GlassFish와 Tomcat를 비교해 보는 시간을 마련했습니다.

많은 차이점을 보이는 두 가지 인기 옵션

GlassFish 커뮤니티가 제공하는 오픈 소스 애플리케이션 서버 GlassFish는 2005년 썬을 통해 선보인 이래로 개발자들 사이에서 큰 인기를 끌고 있습니다. GlassFish는 현재 두 가지 버전의 보급판(GlassFish v2와 새로 발표된 Glassfish v3 Prelude)이 시중에 나와 있으며, 그 중 GlassFish v2는 현재까지 다운로드 횟수가 약 9백만 카피에 달하며 2009년 한 해에만 이미 30만 개의 제품이 등록되었습니다. 여기에 신기능을 추가하고 기존 기능을 더욱 강화한 Glassfish v3 Prelude는 Java나 동적 언어(jRuby 등) 기반의 리치 인터넷 애플리케이션 개발에 더할 나위 없는 최고의 플랫폼으로 평가 받고 있습니다.

GlassFish는 웹 컨테이너의 일종인 Java EE 컨테이너에 속해 있는 반면, Tomcat은 그 자체로 웹 컨테이너를 구성하고 있습니다. 그리고 이러한 두드러진 차이점은 GlassFish에 여러 이점을 가져다 줍니다.

Tomcat 애플리케이션 서버는 Apache 소프트웨어 재단에 의해 개발되었으며, Sun과 JServ 개발자들도 이 프로젝트에 참여한 바 있습니다(초기 코드 드롭을 썬에서 제공). 오픈 소스 러이선스가 적용된 Tomcat은 초기 서버측 Java 도입 단계에서 핵심적인 역할을 담당했으며, 대기업들 간에 오픈 소스 소프트웨어의 붐을 일으키는 데 기여하기도 했습니다.

일반적으로, Tomcat 상에서 구동되는 애플리케이션은 GlassFish에서도 그대로 사용이 가능하나, 두 제품 간에는 애플리케이션의 성능, 확장성, 사용성 및 개발자 생산성에 영향을 미칠 수 있는 중요한 차이점이 있습니다.

이러한 차이점을 이해하기 위해서는 먼저 웹 컨테이너 기술에 대해 살펴보는 것이 중요합니다. 애플리케이션 서버에 속하는 웹 컨테이너는 서블릿, JSP(JavaServer Pages), 기타 웹 티어 컴포넌트의 관리를 담당합니다.

GlassFish는 웹 컨테이너의 일종인 Java EE 컨테이너에 속해 있는 반면, Tomcat은 그 자체로 웹 컨테이너를 구성하고 있습니다. 그리고 이러한 두드러진 차이점은 GlassFish에 여러 이점을 가져다 줍니다.

  • 간편한 마이그레이션. GlassFish v2를 이용하면 Enterprise Java Beans(EJBs), Java Persistence API(JPA), Java Message Service(JMS) 등의 이점을 확실하게 누릴 수 있습니다. 반면에 Tomcat의 경우 상기의 기술을 개별적으로 추가해 줘야 하는 번거로움이 있으며, 개발자는 그 기능을 일일이 구현해서 이들이 제대로 돌아가도록 해야 합니다.
  • 향상된 클러스터링 및 페일오버 지원. GlassFish v2가 제공하는 클러스터링과 정교한 고가용성 기능은 애플리케이션이 엄격한 엔터프라이즈급 SLA(Service Level Agreement)를 충족할 수 있도록 설계되었습니다. GlassFish v3 Prelude는 아직 클러스터링 프로파일이 적용되지 않은 관계로 로드 밸런서를 통해 클러스터링을 지원하고 있습니다.
  • 강력한 관리/모니터링 기능. GlassFish v2와 v3 Prelude 모두 관리 콘솔과 Command Line Interface(CLI)를 이용한 중앙식 관리 방식을 지원하고 있고, 특히 GlassFish v2에서 제공하는 Callflow Monitoring 기능은 애플리케이션 개발자 또는 서버 관리자가 애플리케이션이 가장 많이 사용되는 영역을 파악할 수 있도록 도와줍니다(이 기능은 GlassFish v3에서도 지원됨). 또한 여타 벤더들도 GlassFish Update Center를 통해 GlassFish를 간편하게 설치하고 각자의 소프트웨어를 활용할 수 있습니다. 반면에 Tomcat의 경우 새로운 소프트웨어 사용 시 이를 일일이 구성해주어야 하는 불편함이 있습니다. Update Center는 또한 EJB 3.1 같은 최신 기술을 손쉽게 액세스할 수 있게 해주므로 애플리케이션을 EAR 파일로 패키징하는 대신 EJB를 WAR로 번들링하는 것이 가능합니다.
  • 다양한 스크립팅 언어 지원. GlassFish는 Ruby/JRuby, Python/Jython, Groovy, PHP, JavaScript/Phobos, Scala 같은 다양한 언어들을 지원합니다(또는 지원 예정).

본격 비교: 웹 컨테이너와 관련한 또 다른 차이점

앞서 언급한 일반적인 이점 외에도 GlassFish이 Tomcat에 비해 두드러지는 점은 바로 웹 컨테이너 기능입니다. 몇 가지 예를 들면 다음과 같습니다.

  • v3 Prelude의 애플리케이션 재배치 시 세션 유지 능력은 개발자가 보다 단기간에 Java 웹 애플리케이션을 개발할 수 있도록 도와줍니다.
  • GlassFish v2/v3 Prelude는 서버를 재시작하지 않고 가상 서버와 HTTP Listener를 동적으로 재구성할 수 있게 해주는 반면, Tomcat에서는 리소스 풀을 변경할 경우 보통 애플리케이션 서버를 재시작해야 합니다.
  • v2와 v3 Prelude의 고성능·고확장 Grizzly Framework가 요청/응답 쓰루풋을 극대화합니다. 또한 GlassFish의 하위 웹 티어 레이어가 이 Grizzly Framework을 통해 구현되는데, Java로 제작된 이 프레임워크는 NIO API(확장 네트워크 및 파일 I/O)의 이점을 최대한 활용함으로써 높은 확장성 뿐 아니라 고도의 커스터마이징 능력까지 제공합니다.
  • GlassFish v2와 v3 Prelude는 다양한 성능 최적화 기능을 제공합니다. 그 중 하나로 각 밸브의 호출 방식을 간소화함으로써 Stack Depth를 제한하고 성능을 개선해 주는 밸브 아키텍처 수정판인 "Flattened Valve Invocation"을 들 수 있습니다. 또한 GlassFish v3 Prelude은 Tomcat 스타일의 밸브도 지원합니다.

그 밖에도 썬은 광범위한 확장성 테스트를 통해 Tomcat의 NIO 커넥터와 Glassfish를 비교해 보았습니다. 이 테스트에는 컨테이너에서의 시간 소모를 최소화하기 위해 단순한 서블릿이 이용되었고, 사용자 증가와 관련하여 각종 컨테이너가 지원할 수 있는 능력(작업/초)이 측정되었습니다. 가령 16,000명의 사용자를 기준으로 했을 때, 벤치마크 결과는 다음과 같습니다.

  GLASSFISH TOMCAT
작업/초 6988.9 6615.3
평균 응답 시간 0.242 0.358
최대 응답 시간 1.519 3.693
90% 응답 시간 0.6 0.75

올바른 제품 선택을 위해서는

애플리케이션 서버는 개발팀은 물론이고 기업 운영의 많은 부분에까지 영향을 미치는 중요한 요소입니다. 따라서 애플리케이션 서버 선택은 그 중요도 면에서 고도의 전략적 비즈니스 의사결정을 내리는 것과 맞먹는다고 할 수 있습니다. 이에 우리 썬은 GlassFish가 차세대 애플리케이션을 개발하는 개발자들에게 있어 최상의 옵션이 될 것이라고 자신 있게 주장하는 바이지만, 동시에 다른 대안에 대해서도 신중하게 검토해볼 것을 권하고 싶습니다. GlassFish와 Tomcat에 대한 추가 정보 및 비교 자료를 원하시면 썬 백서를 참조하십시오.

Posted by 자개비
개발관련/자바2009. 2. 27. 17:48
jdk 1.4 이하에서는 Collection 자료 구조를 사용할 경우 값을 넣을 경우 Object타입으로 들어가서
후에 빼서 사용할 때 (String)과 같이 캐스팅을 해야하는 불편함이 있다. jdk1.5(java 5) 이후부터는 이러한 불편함을
해소할 수 있는 기능이 추가되었다. generics라고 하는 것이다.

예를 들어 ArrayList를 사용할 때 다음과 같이 사용한다.

ArrayList<String> list = new ArrayList<String>();

<String>이라고 parameter를 주면 ArrayList에 들어가는 값은 String 타입이라고 정의해주는 것이다.

이렇게 해서 얻어지는 이점은 일단 값에 대한 타입이 명시적으로 드러나므로 명확한다.

또한 캐스팅이 안되는 값이 들어갈 경우 이전에는 컴파일 단계에서 잡아내지 못하고 실행단계에서 Exception이 나서
잘못 된 것을 알 수 있었는데, 이제는 컴파일 단계에서도 잡아내 주기 때문에 디버깅하기가 수월해졌다.

덧붙여 하나 더 얘기하면, java5 이상에서 Collection의 경우 for문을 간결하게 사용할 수 있게 되었다.

예를 들면,

ArrayList<String> list = new ArrayList<String>(); 와 같이 변수를 선언하고 값을 넣은 후 값을 빼내기 위해서
우리는 for 문을 사용한다.

for(Iterator i = list.iterator(); i.hasNext();) {
  String s = i.next();
  System.out.println(s);
}

아래와 같이 하면 좀 더 간결해진다.

for(String s:list) {
  System.out.println(s);
}

풀이를 하자면, list의 개수만큼 루프를 돌면서 값을 빼서 s에 넣으라는 것이다. 여기서 s는 String으로 선언했다.
list에 들어간 값이 앞에서 <String>으로 했기 때문이다. 그렇지 않다면 Object로 해야 할 것이다.






'개발관련 > 자바' 카테고리의 다른 글

[펌]템플릿 형태 JSP  (0) 2010.01.06
[펌][JAVA] static키워드 바로알기  (0) 2009.12.18
[펌]GlassFish와 Tomcat 비교  (0) 2009.06.12
eclipse DB plugin - DBViewer  (1) 2009.02.27
[펌]Statement vs. PreparedStatement  (0) 2009.02.27
Posted by 자개비
개발관련/자바2009. 2. 27. 10:57
eclipse DB plugin을 찾다가 발견했다. 괜찮은 것 같다. 일본에서 만든것이다.
일본이 IT가 우리보다 느리기는 하지만 제대로 하려고 하는 사람들이 많은 것 같다.
우리나라에서도 좋은 plugin을 개발할 수 있는 개발자가 나왔으면 좋겠다.

eclipse의 plugins 폴더에 다운받은 jar 파일을 복사하고 eclipse를 재시작하면 perspective에 추가되어 보인다.

http://www.ne.jp/asahi/zigen/home/plugin/dbviewer/about_jp.html

스크린샷(클릭하면 원본 이미지를 볼 수 있슴)


Posted by 자개비
개발관련/자바2009. 2. 27. 09:36

PreparedStatement를 사용하는 경우 분포도
이 부분은 preparedStatement를 사용하는 경우 값의 분포도에 따라 오히려 낮은 성능을 보이는 경우로 자바서비스넷(www.javaservice.net)에서 김주현씨가 답변한 내용을 일부 수정한 것으로 상당히 이해가 쉽게 정리가 되어 있습니다.


 BIND 변수를 쓸 경우 실행계획이 틀어져서 느려지는 그런 경우를 unsafe literal 이라고 합니다.  예를 들어 다음과 같은 쿼리 문이 있다고 하면  select * from emp where empno = :v1  위의 SQL에서 empno 는 emp의 PK입니다. 따라서 preparedStatement를 써서 BIND 변수를 사용하든지 그렇지 않든지 실행계획은 똑같이 나옵니다. 

 

이런 경우를 safe literal 이라고 합니다.  하지만 쿼리 문이 다음과 같다고 하면  select * from emp where deptno > :v1  위의 경우 :v1이 1 일 경우와 1000일 경우는 분포도가 전혀 다릅니다. 1일 경우에는 대부분의 deptno가 1보다는 크기 때문에 분포도가 넓습니다. 즉, 인덱스를 타지 않는게 더 좋습니다. 하지만 deptno가 1000보다 큰 경우는 거의 없습니다.


 

즉, 분포도가 좁고, 이 경우 인덱스를 타야 합니다.  그러니까 히스토그램이라는 것은 이처럼 실제 :v1 값이 무엇이냐에 따라서 분포도가 달라지는 분포도 정보입니다. 어느 value가 어느 정도의 분포도를 가지고 있는지 하는 정보를 히스토그램이라고 합니다.  문제는 BIND 변수를 쓰면 :v1 이 1이 들어올지 1000이 들어올지에 무관하게 무조건 단 하나의 실행계획만을 만들고 일괄적으로 적용하다가 보니 1000이 들어오는데도 불구하고 인덱스를 타지 않고 Full Scan하는 상황이 나올 수 있습니다.  즉, BIND 변수를 활용하면 히스토그램 정보를 활용할 수 없습니다. 


 

그렇다고 prepatedStatement를 쓰지 말아야 할까요? 아닙니다. 써야 합니다. 위와 같이 불충분한 통계정보(히스토그램)이 없어서 실행계획을 잘못 수립해서 느려지는 경우는 어쩔 수 없는 현재의 옵티마이져의 한계로 보아야 합니다. 이 경우 똑똑한 사람이 힌트 등을 통해서 SQL에게 올바른 실행계획을 가르쳐줘야 합니다.  아주 특수한 경우 위와 같은 이유로 튜닝을 위해 Statement를 쓰는 경우도 있습니다.


 

예를 들어 성별이 남, 여 두 가지일 경우, 그리고 남자가 전체의 2%이며 아주 극소수라고 가정하면 남자일 경우에는 인덱스를 타야 하고, 여자일 경우 인덱스를 타지 말아야 합니다. 이 경우 어차피 distinct 값의 종류가 딱 2가지 이므로 BIND 변수를 쓰지 않아서 하드 파싱을 해봤자 2개의 SQL 종류 밖에는 나오지 않으므로 문제가 없습니다.  select * from user where sex = 'm' select * from user where sex = 'f'  오라클의 Shared pool의 Library cache에는 위와 같이 딱 두 종류의 SQL이 저장되어있어서 재사용되겠지요.  


 


Statement vs. PreparedStatement의 성능 차이 


이 부분은 자바서비스넷(www.javaservice.net) 에서 statement와 preparedstatement 중 어떤 것이 나은지 논쟁 중에 박영록(poci)씨가 “static sql과 dynamic sql의 성능 차이”라는 제목으로 preparedstatement를 사용하는 경우 발생할 수 있는 문제에 대해 정리한 내용을 일부 수정한 것으로 소모적인 논쟁 중에서 빛을 발하는 내용입니다.


 prepared statement에서는 보통 변수를 설정하고 바인딩하는 static sql이 사용되고 statement에서는 쿼리 자체에 조건이 들어가는 dynamic sql이 사용됩니다. (주 - 여기서 언급하고 있는 static/dynamic은 일반적으로 얘기하는 동적 sql과는 다른 의미로, 실행계획이 매번 새로 생성되는지 아닌지에 관한 것입니다.)  preparedStatement가 파싱 타임을 줄여주는 것은 분명히 중요한 장점이지만, static sql을 사용하는데 따르는 퍼포먼스 저하를 생각하지 않을 수가 없습니다.  이를테면 이런 예가 있을 수 있습니다.  dynamic sql을 사용하는 경우 자바에서 다음과 같은 SQL을 생성했다고 가정해봅시다. 


 

SELECT ... FROM   TAB1 WHERE  COL1 LIKE '%' AND    COL2 LIKE '%' AND    COL3 LIKE 'ABC%'  그리고 인덱스가 COL1 + COL2 + COL3 로 걸려 있습니다.  그렇다면 실행계획은 당연히 인덱스에서 COL3 컬럼만을 액세스하게 설정될 것입니다.  그런데 다음과 같은 static sql을 봅시다.  SELECT ... FROM   TAB1 WHERE  COL1 LIKE :A || '%' AND    COL2 LIKE :B || '%' AND    COL3 LIKE :C || '%'  이런 식으로 SQL을 작성했다고 합시다.


 

A, B, C에 어떤 값이 들어올지 모르니까 인덱스의 COL1, COL2, COL3 컬럼을 차례대로 스캔하도록 처리 경로가 잡힙니다. 그런데 조건으로 A, B는 NULL, C에는 'ABC'라는 값이 들어왔습니다. COL3 컬럼만 랜덤으로 액세스해서 테이블로 가면 될 것을 인덱스 풀스캔을 해버립니다.  이와 같이 static sql은 변수에 어떤 값이 바인딩 되는지를 알 수 없기 때문에 가장 일반적인 형태로 실행 계획을 작성합니다. 때문에 통계 자료를 제대로 활용할 수 없고 인덱스 활용 등에서 비효율이 발생할 확률이 높아집니다.  이런 예는 정말로 많습니다. 이를테면 TABLE1의 COL1의 분포도가 값이 'A'인 게 100건, 'B'인 게 100만 건 있다고 할 경우, COL1 = :VAR 와 같이 바인딩을 시켜놓으면 늘 풀스캔을 하게 실행계획을 잡습니다.


 

그런데 :VAR에 'A'가 들어가는 경우 인덱스를 타면 금방 결과가 나올 것을 풀스캔 후에 결과를 리턴하게 되죠.  static sql의 활용으로 파싱 타임을 절약해서 매번 실행 계획을 작성하는 것을 피하는 대가로 좀더 비효율적인 실행 계획이 수립된다는 것이죠. 일반적으로 SQL의 실행과정에서 파싱과 실행 계획 수립 과정은 전체 SQL 수행 시간에서 큰 비중을 차지하지 않습니다. 가장 큰 비중으로 차지하는 것은 테이블에서 로우를 가져오는 과정이고 파싱 시간은 이의 10분의 1에 불과합니다. 그런데, 이 10%의 시간을 줄이자고 90%의 시간에서 비효율이 발생하게 만드는 일이 생길 수 있습니다. 


 

물론 쿼리 생성 단계에서부터 어느 정도는 해결할 수 있는 문제입니다만, 그로 인해 자바 코드와 SQL이 복잡하게 얽히는 문제도 결코 작은 문제가 아니죠.  얼마전 대용량 데이터베이스 I, II를 모두 수강했습니다. 강사분께서 계속 강조하는 말씀이 두 가지가 있었습니다. 첫째는 원리를 이해하고 이론적으로 따져보는 것이 중요하다는 것, 그리고 둘째는 이론적으로 따져서 작성한 SQL에 대해 반드시 실행 계획을 떠보라는 것이었습니다. 상반되는 이야기인 것처럼도 보이고 일반적인 이론과 경험의 조화를 말하는 것처럼도 보이지만 저에게는 이론적인 체계를 먼저 잡아나가고 원리를 따져본 다음에 그 이론을 검증해보라는 말로 들렸습니다.  

 

출처 : http://helols.tistory.com/13

'개발관련 > 자바' 카테고리의 다른 글

[펌]템플릿 형태 JSP  (0) 2010.01.06
[펌][JAVA] static키워드 바로알기  (0) 2009.12.18
[펌]GlassFish와 Tomcat 비교  (0) 2009.06.12
Java 5.x 이상에서 유용한 기능(generic)  (0) 2009.02.27
eclipse DB plugin - DBViewer  (1) 2009.02.27
Posted by 자개비