'else if (개발)'에 해당되는 글 8건
- 2009/12/30 windows C 프로그래밍에서 fopen, fwrite, fread 사용할 때 주의할 점(4)
- 2009/12/05 파이썬만 하다가 갑자기 자바를 했더니(4)
- 2009/08/25 UTF-16 vs UCS-2
- 2009/05/11 c++로 라이브러리를 개발할 때 전방선언(forward declaration) 사용하기(3)
- 2008/11/15 자바 System.out.println vs C printf(5)
- 2008/10/16 부모 클래스의 private 메소드 overriding
- 2008/10/15 Collections.synchronizedCollection 메소드
- 2008/04/20 아파치 가상 호스트와 톰캣의 연결(1)
오늘 오랜만에 자바로 알고리즘 퀴즈를 풀었는데, 몇 줄 되지도 않는 코드가 컴파일 에러가 주루룩난다.
public class RSABreaker {
// x < y
public isSharePrime(int x, int y) { ----> return 타입을 빼먹었다.
for (int i = 2; i <= x; i++) {
if (x % i == 0 && y % i == 0)
return true ----> 세미콜론을 빼먹었다.
}
return false;
}
public int decrypt(int e, int n, int b) {
int m = 1;
for (int i = 2; i < n; i++) {
if !isSharePrime(i, n) ----> if 문에 괄호를 빼먹었다.
m++;
}
d = 1 ----> 자료형을 빼먹었다. 세미콜론을 또 안썼다.
while ((d*e) % m != 1)
d++ ---> 세미콜론을 자꾸 자꾸 빼먹는다.
int res = 1;
for (int i = 0; i < d; i++) {
res = (res * b) % n;
}
return res;
}
}
댓글을 달아 주세요
In computing, UTF-16 (16-bit Unicode Transformation Format) is a variable-length character encoding for Unicode, capable of encoding the entire Unicode repertoire. The encoding form maps each character to a sequence of 16-bit words. Characters are known as code points and the 16-bit words are known as code units. For characters in the Basic Multilingual Plane (BMP) the resulting encoding is a single 16-bit word. For characters in the other planes, the encoding will result in a pair of 16-bit words, together called a surrogate pair.
UTF-16은 모든 영역의 유니코드를 표현할 수 있는 가변길이 유니코드 인코딩이다. (블라블라...) 기본언어판(BMP, U+0000~U+ffff) 영역은 한개의 16-bit 워드로 인코딩되고, 다른 언어판(U+10000 이상) 영역의 문자는 두개의 16-bit 대행 코드 쌍(surrogate pair)로 인코딩된다.
UCS-2 (2-byte Universal Character Set) is an obsolete character encoding which is a predecessor to UTF-16. The UCS-2 encoding form is identical to that of UTF-16, except that it does not support surrogate pairs and therefore can only encode characters in the BMP range U+0000 through U+FFFF. As a consequence it is a fixed-length encoding that always encodes characters into a single 16-bit value.
UCS-2는 UTF-16의 선배격인 인코딩 방식이다. UCS-2는 대행 코드쌍을 지원하지 않는 걸 빼고는 UTF-16과 똑같다. 따라서, 기본언어판(BMP, U+0000~U+ffff)만 인코딩 할 수 있다. 결론적으로 16-bit single word 고정길이 인코딩 방식이다.
| code point | 문자 | UTF-16 | UCS-2 | 심볼 |
| U+007A | z (BMP 영역) | 0X007A | 0X007A | z |
| U+6C34 | 물수(BMP 영역) | 0x6C34 | 0x6C34 | 水 |
| U+1D11E | 높은음자리표 (SMP 영역) |
0XD834 0xDD1E (대행코드 쌍) |
N/A | |
http://ko.wikipedia.org/wiki/UTF-16
http://en.wikipedia.org/wiki/UTF-16/UCS-2
에서 발췌 / 정리
댓글을 달아 주세요
#include "A.h"
#include "B.h"
class Apis {
public:
void publicMethod1();
void publicMethod2();
private:
void privateMethod1(A a);
void privateMethod2(B b);
};
//apis.h 끝
//라이브러리 사용하는 코드
#include "apis.h"
Apis apis;
apis.publicMethod1();
apis.publicMethod2();
라이브러리는 개발이 다 끝나고 사용자에게는 컴파일된 라이브러리 파일 하나와 API 정의를 담고 있는 헤더파일만 전달되는 것이 정상이다. 위 소스에서 말하고 있듯이 라이브러리 내부적으로 무슨 로직을 지지고 볶든지 간에 라이브러리를 사용자가 선언하는 클래스는 Apis라는 클래스 한개이고, 호출하는 메소드는 publicMethod1, publicMethod2 딱 두개 뿐이다.
그런데, apis.h를 include하기 위해서는 반갑지 않은 떨거지들이 따라오게 되는데, A.h와 B.h가 그것들이다.
결국 헤더 파일 두개가 추가로 라이브러리에 포함되어야만 하는 사태다. 근데, 어디 그뿐인가. 만약 A.h와 B.h가 다른 헤더파일을 include하고 있다면? 그것도 같이 들어가야 하네? 그러다가 결국 모든 헤더파일을 다같이 실어 올려야 하는 볼썽 사나운 일이 생기게 된다.
이럴 때는 전방선언(forward declaration)을 사용해야 한다.
//이제 A.h와 B.h를 include할 필요없다.
//전방 선언(forward declaration)
class A;
class B;
class Apis {
public:
void publicMethod1();
void publicMethod2();
private:
void privateMethod1(A a);
void privateMethod2(B b);
};
//apis.h 끝
//apis.cpp
//대신 cpp에서 include해야 한다.
#include "A.h"
#include "B.h"
...
A.h와 B.h를 apis.h가 아닌 apis.cpp에 include함으로써, 공개되는 라이브러리에는 빠져도 상관이 없게 된다.
댓글을 달아 주세요
-
갑자기 웬 c++ 기본 강의를 하세염?
아, 그러고 보니 형은 종종 기교가 없는, 섬세한 기본을 닦게 하는 스타일이셨던 같아염. ㅋㄷㅋㄷ
아, 제가 써놓고 봐도 너무 좋은 수식어다. '기교가 없는데도, 섬세한' ....^^;
아래의 두 코드는 Hello World를 여러번 출력하는 초간단 자바와 C 코드입니다.
#include <stdio.h>
int main() {
int i;
for (i = 0; i < 1000000; i++){
printf("Hello World!\n");
}
return 0;
}
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 1000000; i++) {
System.out.println("Hello World!");
}
}
}
이렇게 간단한 두개의 코드를 컴파일하고 각각 실행시간을 time으로 재는데 단, 출력을 파일로 리다이렉션 합니다. 그 결과 두 프로그램의 실행시간 차이는 무려 다음과 같습니다.
real 0m0.103s
user 0m0.050s
sys 0m0.050s
$ time java Test > output2.txt
real 0m9.885s
user 0m2.660s
sys 0m7.340s
이렇게 차이가 나는 이유는 출력 버퍼링의 차이 때문입니다.
C에서 표준출력은 터미널과 같은 대화형 장치에는 \n을 기준으로 flush를 하는 line buffering을 하지만, 파일로 리다이렉션 할 때는 full buffering으로 전환됩니다. 하지만, 자바의 System.out.println은 항상 line buffering을 하게 됩니다.
언어의 성능의 문제가 아니라 buffering 방식이 다르기 때문에 애초에 공정한 게임이 아닌 것입니다. 공정한 게임을 위해서는 자바 코드를 다음과 같이 수정해야 합니다.
public class Test {
public static void main(String[] args) throws IOException {
//표준출력 full buffering
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
for (int i = 0; i < 1000000; i++) {
bw.write("Hello World!\n");
}
bw.close();
}
}
$ javac Test.java
$ time java Test > output2.txt
real 0m0.294s
user 0m0.260s
sys 0m0.020s
자바와 C 프로그램의 성능을 비교하는데 표준출력을 파일로 리다이렉션 하면서 이 차이를 무시하면 아주 엉뚱한 판단을 하게 되겠죠? 제가 그랬습니다. T.T
댓글을 달아 주세요
-
cout << endl; // Send newline & flush --> 라임의 시험은 이것으로 보여짐
cout << flush; // Flush only
cout << ‘\\n’; // Send newline only
즉,
std::cout << "Hello World!" << std::endl; 하면 라인 단위로 flushing을 하고,
std::cout << "Hello World!\\n"; 하면 C의 printf 처럼 buffering 을 하게됨. -
어? 왜 댓글 안달아놓으셨어요?
컴파일 최적화 옵션 -O2를 넣으면 printf가 더 빨라진다고 하셔야죠. ㅋㄷㅋㄷ
JLS(Java Language Specification)에 보면 overriding에 대해서 다음과 같이 정의되어 있습니다.
An instance method m1 declared in a class C overrides another instance method, m2, declared in class A iff all of the following are true:
1. C is a subclass of A.
2. The signature of m1 is a subsignature (§8.4.2) of the signature of m2.
3. Either
◆ m2 is public, protected or declared with default access in the same package as C, or
◆ m1 overrides a method m3, m3 distinct from m1, m3 distinct from m2, such that m3 overrides m2.
노랗게 칠한 부분을 보면 부모 클래스의 메소드인 m2가 public, protected, 또는 default access 일 경우에 overriding 이라고 얘기하고 있습니다.
아래 코드를 한번 보겠습니다.
class Parent {
public void method1(){
System.out.println("parent method1");
}
public void method2(){
method1();
}
}
class Child extends Parent {
public void method1(){
System.out.println("child method1");
}
}
public class Test {
public static void main(String[] args) {
Child c = new Child();
c.method2();
}
}
overriding의 정의에 맞게 Parent 클래스의 public method1을 Child 클래스에서 정의했습니다.
c 객체의 method2를 호출하는데 Child 클래스에 없으므로 Parent 클래스의 method2를 실행합니다. method2에서 method1을 호출하는데, 현재 이 객체는 Child Type의 객체 이므로, Child 클래스에서 재정의한 mehod1이 호출됩니다. 따라서,
> child method1
과 같이 출력이 됩니다. 그럼 코드를 좀 바꿔 보겠습니다.
class Parent {
private void method1(){
System.out.println("parent method1");
}
public void method2(){
method1();
}
}
class Child extends Parent {
public void method1(){
System.out.println("child method1");
}
}
public class Test {
public static void main(String[] args) {
Child c = new Child();
c.method2();
}
}
부모 클래스의 method1이 private 인 경우입니다. 혹시 overriding 이 안된다고 하니까 컴파일 에러? 컴파일은 잘 됩니다. 하지만 이 경우 실행 결과는
> parent method1
이렇게 Parent 클래스의 method1이 호출됩니다. Parent 클래스의 method1과 Child 클래스의 method1은 이름만 같을 뿐 overriding 된 것이 아닙니다. overriding 된 것이 아니니, Parent의 method2는 Parent의 method1을 그냥 호출 합니다. Child 클래스에 정의되어 있는 method1은 Child 클래스에서 호출하는 코드가 있다면 사용이 되겠죠. 사실 이치로 따져 봐도 private 메소드이면 자식 클래스에서도 접근 불가이므로 이를 overriding 한다는 것이 맞지 않는 일이죠.
위 내용은 Effective Java를 읽던 중 "상속 가능한 클래스의 생성자에서 overriding 될 수 있는 메소드를 호출하지 마라. final 이나 private 메소드만 호출해라" 이런 말이 있어서 찾아 보고 알게 된 내용입니다.
댓글을 달아 주세요
Java Collection Framework의 대부분의 Collection 구현체들은 Thread-Safe하지 않으므로 멀티 스레드 환경이라면 반드시 synchronized block을 적절하게 잡아 주어야 합니다.
그런데, java.util.Collections 클래스의 static 팩토리 메소드인 Collections.synchronizedCollection 메소드를 이용하면 간편하게 Thread-Safe한 Collection 객체를 생성할 수 있습니다.
아래의 코드는 10000개의 스레드에서 1개의 TreeSet에 동시 접근해 Integer 객체의 삽입/삭제를 수행하는 코드로, 심각한 문제를 일으키는 코드입니다.
import java.util.Collection;
import java.util.TreeSet;
public class SynchronizedCollectionEx {
//TreeSet은 Thread-Safe하지 않습니다.
static Collection treeSet = new TreeSet();
public static void main(String[] args){
Thread[] t = new Thread[10000];
for (int i = 0; i < 10000; i++){
t[i] = new Thread(new WriterThread());
t[i].start();
}
}
}
class WriterThread implements Runnable {
public void run() {
for (int i = 0; i < 100; i++){
SynchronizedCollectionEx.treeSet.add(new Integer((int)(Math.random() * 10)));
SynchronizedCollectionEx.treeSet.remove(new Integer((int)(Math.random() * 10)));
}
}
}
위 코드를 컴파일 - 실행하면 다음과 같은 예외가 줄기차게 발생하고, 급기야 deadlock에 빠지기도 합니다. 단일 프로세서 환경에서 실행했을 때는 그렇게 자주 발생하지는 않더군요. 하지만 듀얼 코어에서 돌리니 거의 항상 발생했습니다.
Exception in thread "Thread-119" java.lang.NullPointerException
at java.util.TreeMap.fixAfterInsertion(TreeMap.java:2074)
at java.util.TreeMap.put(TreeMap.java:559)
at java.util.TreeSet.add(TreeSet.java:238)
at WriterThread.run(Test.java:34)
at java.lang.Thread.run(Thread.java:619)
Exception in thread "Thread-732" java.lang.NullPointerException
at java.util.TreeMap.fixAfterInsertion(TreeMap.java:2074)
at java.util.TreeMap.put(TreeMap.java:559)
at java.util.TreeSet.add(TreeSet.java:238)
at WriterThread.run(Test.java:34)
at java.lang.Thread.run(Thread.java:619)
...
코드에서 공유 객체를 쓰거나 읽는 부분에 synchronized 블럭을 잘 잡아주어야 해결되는 문제입니다. 그리고, 또 하나의 간편한 해결책이 공유 객체인 treeSet 객체를 Thread-Safe한 객체로 재탄생 시키는 방법입니다. 이때 이용하는 static 메소드가
Collections.synchronizedCollection(Collection c)
입니다. 이 메소드는 인자로 들어온 Collection 객체를 래핑하여 Thread-Safe한 collection 객체를 반환합니다.
import java.util.Collection;
import java.util.Collections;
import java.util.TreeSet;
public class SynchronizedCollectionEx {
//treeSet은 Thread-Safe 하지 않습니다.
static Collection treeSet = new TreeSet();
//synchronizedSet은 Thread-Safe 합니다. 이 객체를 공유 객체로 사용해야 합니다.
static Collection synchronizedSet = Collections.synchronizedCollection(treeSet);
public static void main(String[] args){
Thread[] t = new Thread[10000];
for (int i = 0; i < 10000; i++){
t[i] = new Thread(new WriterThread());
t[i].start();
}
}
}
class WriterThread implements Runnable {
public void run() {
for (int i = 0; i < 100; i++){
SynchronizedCollectionEx.synchronizedSet.add(new Integer((int)(Math.random() * 10)));
SynchronizedCollectionEx.synchronizedSet.remove(new Integer((int)(Math.random() * 10)));
}
}
}
java.util.Collections 클래스에는 synchronizedCollections 뿐만 아니라, SynchronizedSet, SynchronizedMap 등등 많은 Synchronized... 류의 static 팩토리 메소드를 제공합니다. 쓰는 방법은 크게 다르지 않습니다.
이 글은 스프링노트에서 작성되었습니다.
댓글을 달아 주세요
환경과 작업 목표는 이렇습니다.
서버가 한대이고, 공인 ip도 한개입니다. 도메인 이름은 sragent.pe.kr 이고, 여기에다가 3개의 가상 호스트를 만들어서 사용하기로 계획을 세웠습니다. 그러니까,
blog.sragent.pe.kr : 이 가상 호스트에는 블로그를 위해서 태터툴즈를 설치합니다. 따라서, php를 사용할 수 있는 아파치 웹서버에서 돌아가야 합니다.
lab.sragent.pe.kr : 이 가상 호스트는 java로 만든 데모 프로그램들을 만들어 올릴 계획입니다. 그래서, 톰캣 서버로 연결되어야 합니다.
www.sragent.pe.kr : 여기에는 어떤 페이지를 올려야 할지 아직 미정입니다. 그냥 블로그로 리다이렉션 시켜야 하나 아니면 비활성화 시켜야 하나... 암튼 지금은 index.html 페이지 하나 있습니다.
그리고, 각각의 가상 호스트는 document root가 달라야 하겠지요. 말이 좀 길어진 탓에 다시 간단히 요약하자면 아래와 같습니다.
| 가상호스트 | ip | 서버 | document root |
| www.sragent.pe.kr | 122.49.118.84 | 아파치 | /var/www |
| blog.sragent.pe.kr | 122.49.118.84 | 아파치 | /var/www/blog |
| lab.sragent.pe.kr | 122.49.118.84 | 톰캣 | /usr/share/tomcat5.5/webapps/lab |
구성 방법을 간단히 얘기하자면, 아파치의 가상 호스트 기능을 이용하여 www, blog, lab 세개의 가상 호스트를 설정합니다. 그중에서 lab 호스트는 톰캣과의 연결을 위해서 mod-proxy 모듈을 이용하였습니다. 당연한 얘기지만 dns 서버에 www, blog, lab이 호스트 이름으로 등록되어 있어야 합니다.
여기에서는 아파치와 톰캣의 설치와 실행에 대해서는 생략을 하고, mod-proxy를 이용한 아파치와 톰캣의 연동, 아파치 가상 호스트 설정 부분만 설명하도록 하겠습니다.
mod-proxy를 이용한 아파치와 톰캣의 연동
아파치는 80포트로, 톰캣 htttp connector는 8080 포트로 떠있다고 가정합니다.
그러니까, http://www.sragent.pe.kr 페이지를 열면 아파치 웹서버에 의해서 페이지가 뜨고, http://www.sragent.pe.kr:8080 페이지를 열면 톰캣에 의한 페이지가 뜨는 상황입니다.
우리가 원하는 것은 http://lab.sragent.pe.kr 페이지를 열면 톰캣에 의해 열리는http://www.sragent.pe.kr:8080/lab 페이지가 보이도록 하고 싶은 것입니다. 최종적으로 원하는 것은 저것이지만, 좀 단계적으로 설명하기 위해서 일단 http://www.sragent.per.kr/lab 이라고 하면, http://www.sragent.pe.kr:8080/lab 페이지를 열어주는 것까지만 먼저 해보도록 하겠습니다.
원래 제가 알고 있던 아파치와 톰캣의 연동 방법은 mod_jk 모듈을 이용하는 방법이었는데요, 찾아보니 mod_proxy를 사용하는 방법이 속도도 빠르고, 설정도 훨씬 단순해서 이 방법을 사용하기로 했습니다. 두 방법의 기능 차이는 잘 모르겠습니다. 암튼, mod_proxy를 사용하기 위해 먼저,
httpd.conf 파일에 아래와 같이 세개의 모듈을 load하는 부분을 추가합니다.
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_http_proxy.so
LoadModule proxy_ajp_module modules/mod_ajp_proxy.so
mod_proxy를 사용하는 방법도 http 프로토콜을 이용하는 방법과 ajp 프로토콜을 이용하는 방법이 있는데요, 프로토콜에 따라서 아래 두줄 중에 하나는 지워도 상관이 없습니다. 아파치 모듈을 컴파일 하는 방법에 대해서는 생략하도록 하겠습니다. 저는 Ubuntu server 버전의 리눅스를 사용하고 있는데, 기본적으로 설치를 하니까, 저 아파치 모듈들은 이미 설치가 되어 있더군요. 그래서 설정만 추가했습니다.
다음으로 proxy를 이용한 호출 설정 부분을 추가해야 합니다. 마찬가지로 httpd.conf 파일에 다음과 같이 추가합니다.
ProxyPass /lab http://localhost:8080/lab
ProxyPassReverse /lab http://localhost:8080/lab
ProxyVia On
혹시 ajp 프로토콜을 이용하고자 하고, 톰캣 ajp connector가 8081 포트에서 listen하고 있다면, ajp://localhost:8081/lab 이렇게 수정해 주면 되겠죠.
보는 것처럼 의미가 명쾌합니다. /lab 으로 들어오는 요청은 http://locahost:8080/lab 요청한 결과를 대신 보내줘라 이런 내용입니다.
아파치 가상 호스트 설정
아파치 가상호스트 설정은 제 환경에서는 /etc/apache2/sites-enabled/000-default 파일에 있더군요. 이 파일에 추가해도 되고, httpd.conf 파일에 추가해도 됩니다. 어차피 apache2.conf 파일에서 include되어 지는 파일들이니까요.
저는 000-default 파일을 수정했습니다. 원래는
NameVirtualHost *:80
<VirtualHost *:80>
DocumentRoot /var/www
ServerName www.example.com
...
</VirtualHost>
이렇게 가상호스트 하나만 되어 있는 것을 다음과 같이 수정하고, 추가합니다.
NameVirtualHost *:80
<VirtualHost *:80>
DocumentRoot /var/www
ServerName www.sragent.pe.kr
...
</VirtualHost>
<VirtualHost *:80>
DocumentRoot /var/www/blog
ServerName blog.sragent.pe.kr
...
</VirtualHost>
생략된 부분은 그냥 복사해서 붙이면 됩니다. 이렇게 하면,
www.sragent.pe.kr, blog.sragent.pe.kr 두개의 호스트에 각각 맞는 Document Root 디렉토리를 찾아서 열게 됩니다. 마지막으로, lab.sragent.pe.kr 호스트에 대해서는 tomcat으로 연결하는 부분을 추가해야 하는데요, 이 부분은 위에서 얘기했던 아파치와 톰캣을 mod_proxy로 연결하는 부분을 가상호스트 설정 안에다가 약간 바꿔서 옮겨 적으면 됩니다.
<VirtualHost *:80>
ServerName lab.sragent.pe.kr
ProxyPass / http://localhost:8080/lab/
ProxyPassReverse / http://localhost:8080/lab/
ProxyVia On
</VirtualHost>
맨뒤에 /lab/ 부분이 '/' 로 끝나는 것에 주의해야 합니다. 안 그러면 경로를 못찾습니다.
이렇게 하면 최종적으로 lab.sragent.pe.kr 가상 호스트로 들어온 요청은 http://localhost:8080/lab/ 페이지를 보여주게 됩니다.
이상입니다. 아 그리고 물론 아파치 설정으로 바꿨으면 restart를 당근 해줘야겠지요.
댓글을 달아 주세요
-
들어가주십시오


댓글을 달아 주세요
고생하셨군요 ㅋㅋ
나름 재밌었어^^
AS812에서 같이 놀던(?) 놈임돠
구글링하다가 형 블로그가 구글 최상단에 떠버리는 사태가.. ㅋㅋ
얼굴 못 뵌지 넘 오래~~
잘 지내시나봐요. *^_^*
다시 코딩하러 후다닥~
종민인가? 반갑다^^ 흔적만 남기고 가버리면 어떡하나. 꼬리라도 남겨야지.