출처: http://baiduhix.blogspot.com.br/2015/08/android-how-to-do-findviewbyid-in.html

★ http://www.vogella.com/tutorials/AndroidLifeCycle/article.html

  • Activity and Fragment Lifecycle





  • Activity Lifecycle (출처: http://philosymbol.net/?p=571)
    : 안드로이드 어플리케이션은 자신의 프로세스 수명을 직접 제어하지 않으며, 안드로이드 런타임이 각 어플리케이션의 프로세스와 그 안에 있는 각 activity를 관리한다.
    : 런타임이 프로세스와 activity의 종료와 관리를 다루는 동안, activity의 상태는 자신의 부모 어플리케이션 프로세스의 우선순위를 결정하는데 사용된다.
    : 어플리케이션 우선순위는 런타임이 어플리케이션과 그 안에 실행중인 activity를 종료시킬 가능성에 영향을 미치게된다.

    - 활성(Active)
     : activity가 stack의 최상위에 있을 경우 이 activity는 현재 화면에 보이고 사용자 입력을 받는다.
     : 안드로이드는 무슨 일이 있어도 활성 상태에 있는 activity가 살아있도록 노력하며, 이 activity가 필요로 하는 리소스를 확보하기 위해 필요에 따라 stack의 아래쪽에 있는 activity들을 종료시킬 수 있다.
     : 다른 activity가 활성화되면 기존의 활성 activity는 일시 중지(Pause)된다.

    - 일시 중지(Active)
     : 경우에 따라 activity는 화면에는 보이지만 포커스를 지니지 않을 수 있다.
     : 이 상태는 투명한 activity나 화면 전체를 사용하지 않는 activity가 그 앞에 활성화되어 있는 경우이다.
     : 일시 중지 상태가 되는 경우 activity는 활성 상태인것처럼 다뤄지지만 사용자 입력을 받지 않는다.
     : 극단적인 경우, 안드로이드는 활성 activity를 위한 리소스 확보를 위해 일시 중지 상태의 activity를 종료시킬 수 있을 것이다.
     : 만약 activity가 완전히 가려지게 되면 그 activity는 중지상태가 된다.

    - 중지(Stop)
     : activity는 화면에 보이지 않는다.
     : 이 activity는 모든 상태 및 멤버 정보를 메모리에 남기지만, 만약 시스템이 활성 activity를 위해 메모리를 요청할 경우 리소스 확보를 위한 정리 후보 1순위가 된다.
     : activity가 중지될 때는 데이터와 현재 UI상태를 저장하는 것이 중요하다.
     : activity가 화면 밖으로 나가거나 닫히고 나면 그 activity는 비활성 상태가 된다.

    - 비활성(Inactive)
     : activity는 종료되고 난 이후와 시작되기 이전 비활성 상태에 머문다.
     : 비활성 activity는 activity stack에게 제거되며, 화면에 다시 나타내기 위해서는 재시작되어야 한다.

     



    - Activity 생명주기를 구성하는 메소드

    메소드 

    설명

     onCreate

     액티비티가 최초 생성시에 호출된다. 초기화 설정을 하는 곳으로 보관된 상태의 액티비티가 있다면, 그 상태를 저장중인 Bundle 객체를 받는다. onStart() 메소드가 이어진다. 강제종료가 불가능하다.

     onRestart

     액티비티가 정지 후 다시 시작되기 바로 직전에 호출된다.
    onStart() 메소드가 이어진다. 강제종료가 불가능하다. 

     onStart

     액티비티가 사용자에게 보여지기 직전에 호출된다. 액티비티가 보여지게되면 onResume() 메소드가, 안보이게 되면 onStop() 메소드가 이어진다. 강제종료가 불가능하다.

     onResume

     액티비티가 사용자와 상호작용하기 직전에 호출된다. (스택의 최상위에 위치) onPause() 메소드가 이어진다. 강제종료가 불가능하다.

     onPause

     시스템이 다른 액티비티를 시작하려 할 때 호출된다. 일반적으로 데이터 저장을 하기에 좋은 곳이다. 소스코드의 속도가 빨라야 한다. 왜냐하면 이 메소드가 끝나기 전까지 다음 액티비티가 실행되지 않기 때문인데 액티비티가 되돌아오면 onResume(), 보이지않게되면 onStop()이 이어진다. 강제종료가 불가능하다.

     onStop

     액티비티가 사용자에게 보이지 않을때 호출 된다. 액티비티가 제거되거나 다른 액티비티가 실행되어 해당 액티비티를 덮어버렸을때, 호출된다.
    액티비티가 되돌아오면 onRestart(), 액티비티가 사라지면 onDestroy() 가 이어진다. 강제종료가 가능하다.

     onDestroy

     액티비티 삭제 직전에 호출된다. 액티비티가 받는 마지막 호출 메소드로 시스템이 메모리 확보를 위해 액티비티 인스턴스를 없애버리거나, finish() 메소드가 호출되면 호출되는 메소드이다.
    isFinishing() 메소드로 두 가지를 분기할 수 있다. onStart() 메소드가 이어진다. 강제종료가 불가능하다. 



    - Activity 상태 저장 (출처: http://namsieon.com/286)


    시스템이 액티비티를 강제종료 했을때, 사용자는 이전의 액티비티로 돌아가고 싶을 수 있습니다. 이럴 경우 액티비티가 강제종료 되기 전에 상태를 저장할 수 있는
     
    onSaveInstanceState() 메소드를 구현하면 저장이 가능해 집니다.



    즉, 액티비티가 파괴되기전에 호출되는 메소드 인데요. ( onPause() 호출 이전에 호출됩니다. )
    이 메소드는 이름/값 쌍으로 이루어진 번들 객체(Bundle) 를 인수로 가집니다. 액티비티가 다시 시작되면 번들은 onSaveInstanceState() 와 onStart() 이후에 호출되는 onRestoreInstanceState() 에게 전달됩니다.



    ☞ onSaveInstanceState() , onRestoreInstanceState() 메소드는 생명주기 메소드는 아닙니다.
    따라서 항상 호출되지는 않으며 특정 상황 ( 액티비티 강제종료전에 onSaveInstance() 호출처럼 ) 에서만 호출됩니다. 단, 사용자 액션에 의해 종료될 때는 ( 사용자가 직접종료 ) 호출되지 않습니다.
    - 사용자가 되돌아가지 않을 생각으로 종료한 것으로 판단한 것이겠죠...

    onSaveInstanceState() 는 액티비티의 일시적인 상태 저장을 위한 것이므로 , 데이터 등을 안전하게 저장하려면 onPause() 메소드에서 처리해야 합니다.



  • Fragment
















'Programing > Android' 카테고리의 다른 글

Google I/O - Memory Management For Android  (0) 2016.05.03
Avoiding Memory Leaks  (0) 2016.05.03
Handler, Looper  (0) 2016.04.19
Parcelable vs. Serializable  (0) 2016.03.22
안드로이드 - 자바 시스템 프레임워크  (0) 2016.01.26
출처 : https://realm.io/kr/news/android-thread-looper-handler/

소개

안드로이드의 애플리케이션을 실행하면 시스템은 메인 액티비티를 메모리로 올려 프로세스로 만들며, 이 때 메인 스레드가 자동으로 생성됩니다. 메인 스레드는 안드로이드의 주요 컴퍼넌트를 실행하는 곳이자 UI를 그리거나 갱신하는 일을 담당할 수 있는 유일한 스레드이므로 UI 스레드라고도 불립니다.

안드로이드 화면을 구성하는 뷰나 뷰그룹을 하나의 스레드에서만 담당하는 원칙을 싱글 스레드 모델이라고 합니다. 싱글 스레드 모델의 규칙은 첫째, 메인 스레드(UI 스레드)를 블럭하지 말 것, 둘째, 안드로이드 UI 툴킷은 오직 UI 스레드에서만 접근할 수 있도록 할 것, 이 두 가지입니다. 이런 싱글 스레드 모델의 영향을 고려하지 않으면 애플리케이션의 성능이 저하될 수 있습니다. 긴 시간이 걸리는 작업을 메인 스레드에서 담당한다면 애플리케이션의 반응성이 낮아질 수 있고, 급기야 사용자의 불편함을 방지하고자 시스템이 애플리케이션을 ANR(Appication Not Responding) 상태로 전환시킬 수도 있습니다. 따라서 시간이 걸리는 작업을 하는 코드는 여분의 스레드를 사용하여 메인 스레드에서 분리해야 하고, 자연스럽게 메인 스레드와 다른 스레드가 통신하는 방법이 필요하게 됩니다.

다른 스레드에서 메인 스레드로 접근하기 위해 Looper와 Handler를 사용할 수 있으며, 안드로이드는 Java의 Thread를 좀 더 쉽게 사용할 수 있도록 래핑한 HandlerThread, 더 나아가 Thread나 Message Loop 등의 작동 원리를 크게 고려하지 않고도 사용이 가능한 AsyncTask 등의 클래스를 제공합니다. 이 글에서는 먼저 Thread-Looper-Handler의 개념을 이해하고, 나아가 HandlerThread와 AsyncTask에 대해 정리해보도록 하겠습니다.

Looper와 Handler의 사용 목적

왜 안드로이드는 메인 스레드에서만 UI 작업이 가능하도록 제한할까요? 메인 스레드가 아닌 스레드가 병렬적으로 실행되고 있을 때, 메인 스레드와 다른 스레드, 두 개 이상의 스레드가 동시에 같은 텍스트뷰에 setText()를 시도하는 경우를 생각하면 간단합니다.

둘 중 어느 스레드의 setText()가 적용될지 예측할 수 없고, 사용자는 둘 중 하나의 값만을 볼 수 있어 다른 한 스레드의 결과는 버려집니다. 이같이 두 개 이상의 스레드를 사용할 때의 동기화 이슈를 차단하기 위해서 Looper와 Handler를 사용하게 됩니다.

Looper와 Handler의 작동 원리

먼저 스레드와 Looper, Handler가 어떻게 작동하는지 알아볼까요? 메인 스레드는 내부적으로 Looper를 가지며 그 안에는 Message Queue가 포함됩니다. Message Queue는 스레드가 다른 스레드나 혹은 자기 자신으로부터 전달받은 Message를 기본적으로 선입선출 형식으로 보관하는 Queue입니다. Looper는 Message Queue에서 Message나 Runnable 객체를 차례로 꺼내 Handler가 처리하도록 전달합니다. Handler는 Looper로부터 받은 Message를 실행, 처리하거나 다른 스레드로부터 메시지를 받아서 Message Queue에 넣는 역할을 하는 스레드 간의 통신 장치입니다.

이제 Handler와 Looper, Message Queue에 대해 좀 더 자세히 살펴보겠습니다.

Handler

Handler는 스레드의 Message Queue와 연계하여 Message나 Runnable 객체를 받거나 처리하여 스레드 간의 통신을 할 수 있도록 합니다. Handler 객체는 하나의 스레드와, 해당 스레드의 Message Queue에 종속됩니다. 새로 Handler 객체를 만든 경우 이를 만든 스레드와 해당 스레드의 Message Queue에 바인드됩니다. 다른 스레드가 특정 스레드에게 메시지를 전달하려면 특정 스레드에 속한 Handler의 post나 sendMessage 등의 메서드를 호출하면 됩니다. 
앞서 Message Queue는 전달받은 Message를 선입선출 형식으로 보관한다고 설명했지만, 전달 시점에 다른 메서드를 사용하여 Queue의 맨 위로 보내거나, 원하는 만큼 Message나 Runnable 객체의 전송을 지연시킬 수도 있습니다. 자주 쓰이는 Handler의 메서드를 아래 표에 정리했습니다.

리턴값메서드명인자설명
voidhandleMessageMessage msgLooper가 Message Queue에서 꺼내준 Message나 Runnable 객체를 처리 
(상속 시 구현 필수)
final booleanpostRunnable rMessage Queue에 Runnable r을 전달
final booleansendMessageMessage msgMessage Queue에 Message msg를 전달
final booleanpostAtFrontOfQueueRunnable rMessage Queue의 맨 앞에 Runnable r을 전달
final booleansendMessageAtFrontOfQueueMessage msgMessage Queue의 맨 앞에 Message msg를 전달
final booleanpostDelayedRunnable r, long delayMillisdelayMillis만큼 지연 후Message Queue에 Runnable r을 전달
final booleansendMessageDelayedMessage msg, long delayMillisdelayMillis만큼 지연 후Message Queue에 Message msg를 전달

외부, 혹은 자기 스레드로부터 받은 메시지를 어떤 식으로 처리할 지는 handleMessage() 메서드를 구현하여 정합니다. sendMessage()나 post()로 특정 Handler에게 메시지를 전달할 수 있고, 재귀적인 호출도 가능하므로 딜레이를 이용한 타이머나 스케줄링 역할도 할 수 있어 편리합니다.

Looper Message Queue

Looper는 무한히 루프를 돌며 자신이 속한 스레드의 Message Queue에 들어온 Message나 Runnable 객체를 차례로 꺼내서 이를 처리할 Handler에 전달하는 역할을 합니다. 메인 스레드는 Looper가 기본적으로 생성돼 있지만, 새로 생성한 스레드는 기본적으로 Looper를 가지고 있지 않고, 단지 run 메서드만 실행한 후 종료하기 때문에 메시지를 받을 수 없습니다. 따라서 기본 스레드에서 메시지를 전달받으려면 prepare() 메서드를 통해 Looper를 생성하고, loop() 메서드를 통해 Looper가 무한히 루프를 돌며 Message Queue에 쌓인 Message나 Runnable 객체를 꺼내 Handler에 전달하도록 합니다. 이렇게 활성화된 Looper는 quit()이나 quitSafely() 메서드로 중단할 수 있습니다. quit() 메서드가 호출되면 Looper는 즉시 종료되고, quitSafely() 메서드가 호출되면 현재 Message Queue에 쌓인 메시지들을 처리한 후 종료됩니다.

Message Runnable

Message란 스레드 간 통신할 내용을 담는 객체이자 Queue에 들어갈 일감의 단위로 Handler를 통해 보낼 수 있습니다. 일반적으로 Message가 필요할 때 새 Message 객체를 생성하면 성능 이슈가 생길 수 있으므로 안드로이드가 시스템에 만들어 둔 Message Pool의 객체를 재사용합니다. obtain() 메서드는 빈 Message 객체를, obtain(Handler h, int what …)은 목적 handler와 다른 인자들을 담은 Message 객체를 리턴합니다.
Runnable을 설명하려면 스레드를 만드는 두 가지 방법부터 말씀드려야 합니다. 새 스레드는 Thread() 생성자로 만들어서 내부적으로 run()을 구현하던지, Thread(Runnable runnable) 생성자로 만들어서 Runnable 인터페이스를 구현한 객체를 생성하여 전달하던지 둘 중 하나의 방법으로 생성하게 됩니다. 후자에서 사용하는 것이 Runnable로 스레드의 run() 메서드를 분리한 것입니다. 따라서 Runnable 인터페이스는 run() 추상 메서드를 가지고 있으므로 상속받은 클래스는 run()코드를 반드시 구현해야 합니다.
앞서 언급한대로 Message가 int나 Object같이 스레드 간 통신할 내용을 담는다면, Runnable은 실행할 run() 메서드와 그 내부에서 실행될 코드를 담는다는 차이점이 있습니다.

HandlerThread

Looper에서 언급했듯이 안드로이드의 스레드는 Java의 스레드를 사용하기 때문에 안드로이드에서 도입한 Looper를 기본으로 가지지 않는다는 불편함이 있습니다. 이 같은 불편함을 개선하기 위해 생성할 때 Looper를 자동으로 보유한 클래스를 제공하는데, 이것이 바로 HandlerThread입니다.
HandlerThread는 일반적인 스레드를 확장한 클래스로 내부에 반복해서 루프를 도는 Looper를 가집니다. 자동으로 Looper 내부의 Message Queue도 생성되므로 이를 통해 스레드로 Message나 Runnable을 전달받을 수 있습니다.

AsyncTask

AsyncTask는 스레드나 메시지 루프 등의 작동 원리를 몰라도 하나의 클래스에서 UI작업과 backgrond 작업을 쉽게 할 수 있도록 안드로이드에서 제공하는 클래스입니다. 캡슐화가 잘 되어 있기 때문에 사용시 코드 가독성이 증대되는 장점이 있으며, 태스크 스케쥴을 관리할 수 있는 콜백 메서드를 제공하고, 필요할 때 쉽게 UI 갱신도 가능하며 작업 취소도 쉽습니다. 따라서 리스트에 보여주기 위한 데이터 다운로드 등 UI와 관련된 독립된 작업을 실행할 경우 AsyncTask로 간단하게 구현할 수 있습니다.

그림: AsyncTask의 구조

그러나 AsyncTask를 사용해서 스케줄링 할 수 있는 작업 수의 제한이 있고, 몇 초 정도의 짧은 작업에서만 이상적으로 동작한다는 한계가 있습니다. 또한, 안드로이드의 버전 별로 병렬 처리 동작이 다르므로 허니콤 이후 버전에서 멀티 스레드로 병렬적인 동작을 원한다면 AsyncTask를 실행할 때 AsyncTask.THREAD_POOL_EXECUTOR 스케줄러를 지정해야 합니다. 
한편 앞서 살펴본 Handler와 Looper를 사용한다면 작동 원리를 고려해야 하며 구현을 직접 해야 하고 코드가 복잡해져서 가독성을 저해한다는 단점이 있지만 그만큼 개발 범위가 자유롭습니다. 또한 UI 스레드에서만 작업하지 않아도 되므로 보다 많은 자율성을 가지고 코드를 제어하기를 원한다면 Handler나 HandlerThread 사용을 고려해 보세요.

※요약

인라인 함수는 프로그램의 실행 속도를 높이기 위해 추가된 기능이며 C언어의 매크로 함수와 비교된다.


(개발자 입장에서)일반 함수와 인라인 함수의 가장 큰 차이점은 함수의 호출 방식이다.

일반 함수의 호출 방법은 프로그램 실행 중 함수를 실행해야하면 해당 함수의 주소로 점프시켰다가, 함수의 처리가 종결되면 다시 원래의 자리로 돌아오는 것이다.

이렇게 앞뒤로 점프를 수행하고, 점프할 위치를 기억하려면 함수를 사용하는데 시간이 많이 걸린다.


인라인 함수는 컴파일된 함수 코드가 프로그램의 코드 안에 직접 삽입되어진다.

이 말은 컴파일러가 함수를 호출하는 대신, 그에 대응하는 함수 코드로 대체한다는 것을 의미하며 함수 호출없이 삽입된 함수 코드를 그 자리에서 처리하므로 해당 함수를 수행하기 위해 프로그램이 다른 주소로 점프했다가 되돌아 올 필요가 없어 속도면에서 유리하다.



일반 함수 어셈블리어


인라인 함수 어셈블리어



※특징

 - 인라인 함수를 사용하려면 함수 선언 앞에 inline이라는 키워드를 붙이거나 함수 정의 앞에 inline이라는 키워드를 붙인다.

 - 클래스 멤버 함수가 inline을 사용하려면, 함수 정의의 위치가 *.h에 있어야 한다. 안 그러면 확인할 수 없는 외부 참조라고 뜬다.

 - 프로그래머가 inline 선언을 해도 컴파일러가 인라인화를 거부할 수 있다.

 - 프로그래머가 inline 선언을 안 해도 컴파일러가 인라인화를 할 수 있다.

 - 함수의 덩치가 크거나 재귀호출이면 inline 요구를 거절하는 컴파일러도 있다.

 - 함수 코드의 수행 시간이 짧고 빈번하게 호출되는 함수가 아니라면, 인라인 함수로 인한 절대적인 시간 절약은 그다지 크지 않다.



※장점

 - 함수가 인라인화 되어 성능의 향상으로 이어질 수 있다.



※단점

 - 메모리 사용 측면에서는 인라인 함수가 일반 함수보다 불리하다.

   이유는 어떤 프로그램에서 인라인 함수를 열 번 호출한다면, 

   프로그램은 그 함수의 사본을 프로그램의 코드 안에 열 번이나 삽입해야 하기 때문이다.

 - 매크로 함수와 달리 자료형에 독립적이지 못 하다. 단, 템플릿을 이용하면 자료형에 독립적으로 사용할 수 있다.



※예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
 
using namespace std;
 
inline void Test( int nNum1 );
 
int main( )
{
    Test( 2 );
 
    return 0;
}
 
void Test( int nNum1 )
{
    int nResult = nNum1;
}


출처:  http://ohyecloudy.com/pnotes/archives/294/


컨테이너 정렬이 필요할 때, sort를 사용하고 순서가 유지돼야 하면 stable_sort를 사용했다. partial_sort와 nth_element는 한 번도 사용해본 적이 없는데, 일부분만 정렬이 필요하거나 몇 번째 원소를 뽑을 때 유용하게 사용할 수 있을 것 같다. 예를 들면 최고 작은 수 10개만 차례대로 혹은 차례 상관없이 뽑는다던지, 9번째로 작은 수만 뽑는다던지 할 때에 사용하면 좋을 거 같다. 구현하는데 시간을 추가로 쓰는 것도 아니고 이미 구현되어 있는데, 딱 필요한 만큼만 정렬해서 시간을 절약할 수 있다. 아무리 작은 시간이라 하더라도 그냥 낭비하는 건 죄를 짓는 거.

nth_element는 정렬 기준에 따라 몇 번째 원소만 정확히 뽑아준다. 원소를 기준으로 정렬 기준에 맞게 좌우를 나눠주기는 하는데, 그 원소들 사이에 정렬은 되어 있지 않은 상태이다. 

partial_sort는 시작점부터 지정한 위치까지만 정렬해준다. 나머지는 정렬되지 않은 상태로 놔둔다. sort와 stable_sort는 시작점에서 끝점까지 정렬하는데, 정렬되고 난 뒤 stable을 보장 여부가 다르다. 동일한 정렬 기준을 가진 녀석들의 순서가 정렬 후에도 바뀌지 않으면 stable하다고 하는데, 뒤에 소스 코드 예를 보면 단박에 이해된다. 참고로 merge sortinsertion sort가 대표적인 stable sort 알고리즘이고 unstable sort의 대표적인 알고리즘은 quicksort다.

수행 시간은 nth_element < partial_sort < sort < stable_sort 이다.

sort, stable_sort, partial_sort 테스트 소스 코드

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

////////////////////////////////////////////////////////////////////////////////
// Item
////////////////////////////////////////////////////////////////////////////////
struct Item
{
    int     num;
    char    tag;

    explicit Item(int n, char t = ' ') : num(n), tag(t) {}
};

inline bool operator< (const Item& lhs, const Item& rhs)
{
    return lhs.num < rhs.num;    // 숫자만 비교한다.
}

inline std::ostream& operator<< (std::ostream& ost, const Item& item)
{
    ost << item.num << item.tag;
    return ost;
}

정렬 알고리즘에서 호출해주는 operator < 는 숫자만 비교한다. tag는 stable하게 정렬되는지 확인하는 용도로 사용한다.

////////////////////////////////////////////////////////////////////////////////
// main
////////////////////////////////////////////////////////////////////////////////
int main()
{
    const Item itemArray[40] =
    {
        Item (-4, ' '), Item (16, ' '),
        Item (17, ' '), Item (-3, 's'),
        Item (14, ' '), Item (-6, ' '),
        Item (-1, ' '), Item (-3, 't'),
        Item (23, ' '), Item (-3, 'a'),
        Item (-2, ' '), Item (-7, ' '),
        Item (-3, 'b'), Item (-8, ' '),
        Item (11, ' '), Item (-3, 'l'),
        Item (15, ' '), Item (-5, ' '),
        Item (-3, 'e'), Item (15, ' '),
        Item (-4, ' '), Item (16, ' '),
        Item (17, ' '), Item (-3, 's'),
        Item (14, ' '), Item (-6, ' '),
        Item (-1, ' '), Item (-3, 't'),
        Item (23, ' '), Item (-3, 'a'),
        Item (-2, ' '), Item (-7, ' '),
        Item (-3, 'b'), Item (-8, ' '),
        Item (11, ' '), Item (-3, 'l'),
        Item (15, ' '), Item (-5, ' '),
        Item (-3, 'e'), Item (15, ' ')
    };

    typedef std::vector<Item> ItemVector;

    ItemVector v0(
        itemArray, itemArray + sizeof(itemArray) / sizeof(itemArray[0]));
    ItemVector v1(v0.begin(), v0.end());
    ItemVector v2(v0.begin(), v0.end());

    std::sort (v0.begin(), v0.end());
    std::stable_sort (v1.begin(), v1.end());
    std::partial_sort (v2.begin(), v2.begin() + 10, v2.end());

    std::cout << "### sort(begin, end)\n";
    std::copy (v0.begin(), v0.end(), std::ostream_iterator<Item>(std::cout, " "));
    std::cout << "\n\n### stable_sort(begin, end)\n";
    std::copy (v1.begin(), v1.end(), std::ostream_iterator<Item>(std::cout, " "));
    std::cout << "\n\n### partial_sort(begin, begin+10, end)\n";
    std::copy (v2.begin(), v2.end(), std::ostream_iterator<Item>(std::cout, " "));

    return 0;
}

20개 정도로 하니깐 sort로 정렬해도 결과가 stable해서 40개로 늘렸다. VS2005의 딩컴웨어 STL에서는 원소 개수가 32개 보다 작으면 insertion sort를 하기 때문에 20개로 sort를 호출하면 stable한 결과가 나온다. 정렬 알고리즘은 원소를 변경하기 때문에 복사해서 똑같은 원소를 가진 벡터를 3개 만들고 각각을 정렬한 다음 출력했다.

sort는 stablestable의 순서가 엉켜있고 stable_sort는 제대로 출력하고 있다.partial_sort는 [begin, begin+10) 까지만 정렬을 한다.

nth_element 테스트 소스 코드

nth_element 같은 경우는 위와 같은 예제로 하면 확인이 어려워 따로 테스트 코드를 만들었다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>

int main()
{
    // VS 2005에서 원소 개수가 32개 이하면 Insertion sort를 한다.
    static const int INSERTION_SORT_THRESHOLD = 33;
    std::vector<int> v;
    v.reserve(INSERTION_SORT_THRESHOLD);

    for (int i = 0; i < INSERTION_SORT_THRESHOLD; ++i)    v.push_back(i);
    std::random_shuffle(v.begin(), v.end());

    std::cout << "### before\n";
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;

    std::cout << "### nth_element(begin, begin+12, end)\n";
    std::nth_element(v.begin(), v.begin()+12, v.end());
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
}

VS 2005의 딩컴웨어 STL 구현을 보면 분할 정복 알고리즘(Divide and conquer algorithm)을 사용해서 구현했는데, 여기서도 sort와 같이 분할한 구역 원소 개수가 32개 이하이면 insertion sort을 한다. 그래서 출력해보면 12 앞에 숫자들이 정렬된 상태로 보인다. 처음에 원소를 10개로 잡아 놓고 테스트를 했는데, 전체가 정렬돼서 무척 의아했다. 궁금해서 소스코드를 보니 원소 개수가 32개 이하이면 insertion sort로 정렬하더라.

STLport 5.1.3 으로 돌려본 결과. insertion sort를 하는 threshold가 3이다.


'Programing > C++' 카테고리의 다른 글

inline 함수  (0) 2016.04.12

출처 - http://aroundck.tistory.com/2477

 android, Parcelable vs. Serializable

 

[android] Parcelable vs Serializable


Serializable 은 Java 만 아는 사람이라면 쉽게 알 수 있는 serialization 방법.

그냥 Serializable 을 implementation 만 해주면, serialize 가 필요한 순간에 알아서 serialze 해주는 편리한marker interface.


그러나, mobile 시대가 강림하면서 등장한 유망한 어린이(?) 가 있으니 그는 바로 Parcelable.

이 녀석은 IPC ( Inter Process Communication ) 에 최적화된 녀석으로.

Serialize 보다 속도가 빠르다.

물론, 해야 하는 일은 Serialize 보다 훨씬 많다.

직접 serialize 되어야 할 녀석들을 선별해서 그것을 쓰고 읽는 작업을 해주어야 한다.


그럼 왜 serialization 이 parcelable 보다 속도가 느릴까?

그 이유는, serialization 은 reflection 방법을 사용하여 serialization 을 하는데,

parcelable 은 프로그래머가 직접 바로 setting 을 해주기 때문에 빠른 것이다.

( reflection 이 성능이슈를 야기할 수 있다는 것은 이미 알고 있을꺼라 생각한다.. )


다행스럽게도 둘의 성능차이가 얼마나 날까 테스트 결과를 찾아봤는데

이런 결과를 볼 수 있었다.


[android] Parcelable vs Serializable이미지 출처 : http://www.developerphil.com/parcelable-vs-serializable/

결론적으로 보면,

Serialization 도 그렇게 느리지는 않지만, Parcelable 이 훨씬 빠르다.

정말 많은 뭉태기의 property 들이 한 클래스에 있는 경우는 드물겠지만,

그런 경우라면 Parcelable 의 성능이 훨씬 빠르겠고..

IPC 가 많이 발생하는 경우라면, 가랑비에 옷 젖는 줄 모른다고, 작은 성능 차이가 결국 최적화에 엄청난 영향을 미칠 수 있다. 결국 정말 특별한 이유가 없다면, 사실 귀찮더라도 Serializable 보다는 Parcelable 을 사용하는 것이 추천된다.



  • Exception
    package kr.co.ioacademy;
    // 자바의 예외 3가지
    // 1. 점검 지정 예외     : 복구가 가능한 상황
    // 2. 무점검 지정 예외    : 복구가 불가능한 상황
    //  1) 실행 시점 예외(Runtime Exception) - 사용자
    //  2) 오류(Error) - JVM
    
    // API 사용자에게 점검 지정 예외를 준다는 것은 그 상태를 복구할 권한을 준다는 것이다.
    // (무시할 수 있지만 무시하면 안된다)
    
    // 실행 시점 예외와 오류의 동작 방식은 동일하다.
    // -> 둘다 catch 할 필요가 없다. 처리해서도 안된다.
    
    
    class Engine {}
    
    class Car {
    
      private static Class<Engine> engineClass = Engine.class;
    //  private Engine engine = engineClass.newInstance();
    //  // 2. 인스턴스 필드의 초기화에서 발생하는 예외는 생성자에서 처리해야 한다.
    //  public Car() throws Exception {
    //  }
    
      private Engine engine = newEngine();
      private static Engine newEngine() {
        try {
          return engineClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
          e.printStackTrace();
        }
        return null;
      }
    
      /*
      // 1. 인스턴스 필드가 생성자 호출보다 먼저 일어난다.
      private Car instance = new Car();
      public Car() throws Exception {
        throw new Exception("Failed to create");
      }
      */
    }
    
    public class Example9 {
      public static void main(String[] args) {
        try {
          Car car = new Car();
          System.out.println("Succeed");
        } catch (Exception e) {
          System.out.println("Failed");
        }
      }
    }

  • Assertion
    : jdk 1.4부터 도입
    : Assertion은 불리언 식(expression)을 포함하고 있는 문장으로서, 프로그래머는 그 문장이 실행될 경우 불리언 식이 참이라고 단언할 수 있다. (JSR, Java Specification Request) 
    : Assertion은 개발자가 참이라고 가정하는 상태를 명시하기 위해서 사용된다. (예를 들어, 어떤 메소드가 파라미터로 양수만 입력받아야 한다고 확신한다면, Assertion을 사용하여 그 사실을(즉, 파라미터가 양수라는 것을) 명시할 수 있다.)

    // jdk 1.4
    // javac -source 1.4 AssertionTest.java 
    // JRE 클래스가 Assertion을 포함하는지 여부에 상관없이 기본적으로 Assertion 기능을 사용하지 않는다.
    // java -ea AssertionTest
    
    
    // 문법
    // assert [boolean 식];
    // assert [boolean 식]:[표현식];
    
    // 사용해선 안 되는 경우
    // public 메소드의 파라미터를 검사하는 경우
    // 올바른 수행을 위해 필요한 작업을 수행하는 경우
    // ex) assert checkName(); -> boolean checked = checkName();
    // 							  assert checked;
    
    public class AssertionTest {
    	// Assert 기능 강제적으로 사용하도록 유도
    	static { 
    		boolean assertsEnabled = false;
    		assert assertsEnabled = true;        if (!assertsEnabled)
    			throw new RuntimeException("Assertion 기능이 사용가능해야 합니다!");
    	}    
    
    	/*    
        // post-condition check
    	Object getObj(Object obj) {
    		assert obj != null : "obj cannot be null";
    
    		return obj;
    	}
     
        // pre-condition check
    	void register(MemberInfo member) {
    		assert Member != null:"MemberInfo cannot be null";
    		assert member.getId() != null && !member.getId().equals("");
    
    	        ...
    	}
    	 */
    
    	public static void main(String[] args) {
    		char operator = '%';                  // assumed either '+', '-', '*', '/' only
    		int operand1 = 5, operand2 = 6, result = 0;
    		switch (operator) {
    			case '+': result = operand1 + operand2; break;
    			case '-': result = operand1 - operand2; break;
    			case '*': result = operand1 * operand2; break;
    			case '/': result = operand1 / operand2; break;
    			default: assert false : "Unknown operator: " + operator;  // not plausible here
    		}
    		System.out.println(operand1 + " " + operator + " " + operand2 + " = " + result);
    	}
    }
    
    참고 : http://javacan.tistory.com/entry/79, https://www3.ntu.edu.sg/home/ehchua/programming/java/J5a_ExceptionAssert.html


'Programing > Java' 카테고리의 다른 글

Effective Java - Annotation  (0) 2016.03.08
Effective Java - Reflection  (0) 2016.03.08
Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
  • Anotation
    - Java Annotation?
     : 어노테이션은 어노테이션된 요소들의 행동으로부터 프로그램의 행위를 추출하여, 필요하다면 컴파일러나 VM이 상호 의존적인 코드를 생성하는, 프로그램 요소와 메타 태그에 관계된 매커니즘.
     : 사전적으로는 "주석"이라는 의미를 가지고 있으며, 의미대로 자바 코드에 주석처럼 달아 특수한 의미를 부여해준다. 이 특별한 의미는 컴파일 타임 또는 런타임에 해석 될 수 있다.


    - .java부터 .class까지

  •  : .java => Parser => Type checker => [Annotation Checker] => Class File writer => .class


    - 어노테이션 규칙

     : @interface + 어노테이션 이름

     : 어노테이션 소스코드 내부의 메소드 선언은 매개변수를 지닐 수 없다.

     : 어노테이션 소스코드 내부의 메소드 선언은 clauses를 throw 할 수 없다.

     : 메소드의 반환 타입은 primitives, String, Class, enum과 primitives배열, String배열, Class배열, enum배열 중 하나이다.


    - 분류

     1. marker ; @AnnotationTypeName
      : 멤버 변수가 없으며, 단순히 표식으로서 사용되는 어노테이션이다. 컴파일러에게 어떤 의미를 전달한다.

     2. single-element ; @AnnotationTypeName(single-element)
      : 멤버로 단일변수만을 갖는 어노테이션이다. 단일변수 밖에 없기 때문에 (값)만을 명시하여 데이터를 전달할 수 있다.

     3. normal, full-value, multi-value ; @AnnotationName(element=value, element=value, ...)
      : 멤버로 둘 이상의 변수를 갖는 어노테이션으로, 데이터를 (값=쌍)의 형태로 전달한다.


    Built-in Annotation

    == java.lang.annotation (meta-annotation)

    @Retention

     : Retention은 어노테이션이 얼마나 오랫동안 유지되는지에 대해, JVM이 어떻게 사용자 어노테이션을 다루어야 하는지를 서술합니다.

     * SOURCE - 어노테이션이 컴파일 타임시 버려진다는 것을 의미합니다. retention정책이 source로 정의되어 있으면, 클래스 파일은 어노테이션을 지니지 못합니다.

     * CLASS - 어노테이션이 생성된 클래스 파일에서 나타날 것이라는 것을 의미합니다. 그러나 런타임시에는 이 어노테이션을 이용하지 못합니다.

     * RUNTIME- 이는 런타임시 JVM에서 어노테이션의 이용이 가능하다는 것을 의미합니다. 이러한 어노테이션을 읽는 사용자 로직을 가짐으로써 런타임시 무언가를 할 수 있습니다.


    @Target

     : Target은 어디에 어노테이션을 넣을 수 있는지를 서술합니다. field, method, class가 정의된 곳에 어노테이션을 넣을 수 있습니다.

     * TYPE - class, interface, enumeration에 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * METHOD - method 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * PARAMETER - parameter 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * PACKAGE - package 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * FIELD - field 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * LOCAL_VARIABLE - 지역 변수 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * CONSTRUCTOR - 생성자에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * ANNOTATION_TYPE - 어노테이션 타입에만 어노테이션을 적용할 수 있다는 것을 의미합니다.


    @Inherited

     - 기본적으로 어노테이션은 상속되지 않습니다. 따라서 상속을 원한다면, 어노테이션을 Inherited 해야 합니다. Inherited 어노테이션이 사용자 어노테이션에 놓여야 하며, 이는 클래스에만 효과가 있습니다. (즉, 상속받는 클래스도 같은 어노테이션 사용)


    @Documented

     - 어노테이션이 기본으로 javadoc 및 유사한 툴에 의해 문서화 되는 것을 나타냅니다. 이러한 형태는 타입의 선언에 주석을 달기 위해 사용합니다. 어노테이션은 클라이언트에 의해 어노테이션된 요소의 사용에 영향을 미치게 됩니다. 타입의 선언에 Documented 어노테이션을 붙인 경우, 그 어노테이션은 어노테이션된 요소의 공개 API중 한 부분이 됩니다.


    == java.lang

    @Override
     : 현재 메소드가 수퍼클래스의 메소드를 오버라이드한 메소드임을 컴파일러에게 명시한다. 만일 수퍼 클래스에 해당하는 메소드가 없으면 컴파일러가 인지하고 에러를 발생시켜 준다.

    @Deprecated
     : 마커 오노테이션으로 차후 버전에 지원되지 않을 수 있기 때문에 더 이상 사용되지 말아야할 메소드를 나타낸다. 특이하게 더이상 사용되지 말아야할 메소드와 같은 라인상에 놓여져야 한다.

    @SuppressWarnings
     : 의미대로 경고를 제거하는 어노테이션이다. Object형을 엘리먼트로 하는 컬렉션을 사용하면, 컴파일러 경고가 발생하는데 이 어노테이션을 사용하여 프로그래머의 의도적인 Object형 사용임을 알려 경고를 제거할 수 있다.

    출처: http://www-01.ibm.com/support/knowledgecenter/SSQ2R2_9.1.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm?lang=ko
    Java 5.0부터 java.lang.SuppressWarning 어노테이션을 사용하여 컴파일 단위의 서브세트와 관련된 컴파일 경고를 사용하지 않도록 설정할 수 있습니다.

    @SuppressWarning("unused") public void foo() { String s; }

    어노테이션이 없으면 컴파일러에서 로컬 변수 s를 사용할 수 없습니다. 어노테이션을 사용하여 컴파일러는 이 경고를 foo 메소드에 대해 로컬에서 무시합니다. 이러한 경우 동일한 컴파일 단위 또는 동일한 프로젝트의 다른 위치에 경고를 보관할 수 있습니다.

    SuppressWarnings 어노테이션 내부에서 사용할 수 있는 토큰 목록은 다음과 같습니다.

    • all 모든 경고를 억제합니다.
    • boxing boxing/unboxing 오퍼레이션과 관련된 경고를 억제합니다.
    • cast 캐스트 오퍼레이션과 관련된 경고를 억제합니다.
    • dep-ann 권장되지 않는 어노테이션과 관련된 경고를 억제합니다.
    • deprecation 권장되지 않는 기능과 관련된 경고를 억제합니다.
    • fallthrough switch 문에서 누락된 break 문과 관련된 경고를 억제합니다.
    • finally 리턴되지 않는 마지막 블록과 관련된 경고를 억제합니다.
    • hiding 변수를 숨기는 로컬과 관련된 경고를 억제합니다.
    • incomplete-switch switch 문에서 누락된 항목과 관련된 경고를 억제합니다(enum case).
    • javadoc javadoc 경고와 관련된 경고를 억제합니다.
    • nls 비nls 문자열 리터럴과 관련된 경고를 억제합니다.
    • null 널(null) 분석과 관련된 경고를 억제합니다.
    • rawtypes 원시 유형 사용법과 관련된 경고를 억제합니다.
    • resource 닫기 가능 유형의 자원 사용에 관련된 경고 억제
    • restriction 올바르지 않거나 금지된 참조 사용법과 관련된 경고를 억제합니다.
    • serial 직렬화 가능 클래스에 대한 누락된 serialVersionUID 필드와 관련된 경고를 억제합니다.
    • static-access 잘못된 정적 액세스와 관련된 경고를 억제합니다.
    • static-method static으로 선언될 수 있는 메소드와 관련된 경고를 억제합니다.
    • super 수퍼 호출을 사용하지 않는 메소드 겹쳐쓰기와 관련된 경고를 억제합니다.
    • synthetic-access 내부 클래스로부터의 최적화되지 않은 액세스와 관련된 경고를 억제합니다.
    • sync-override 동기화된 메소드를 오버라이드하는 경우 누락된 동기화로 인한 경고 억제
    • unchecked 미확인 오퍼레이션과 관련된 경고를 억제합니다.
    • unqualified-field-access 규정되지 않은 필드 액세스와 관련된 경고를 억제합니다.
    • unused 사용하지 않은 코드 및 불필요한 코드와 관련된 경고를 억제합니다.

    참고: http://netpyoung.tistory.com/100, http://hiddenviewer.tistory.com/88, https://en.wikibooks.org/wiki/Java_Programming/Annotations/Meta-Annotations

    - Custom Annotation

     : 클래스와 같이 어노테이션을 임의로 정의하여 사용할 수 있다. 어노테이션은 interface 키워드 앞에 @를 붙여 표시한다.
     : @interface InProgress{}

    - 예제

    package kr.co.ioacademy; // ioacademy 윤찬식 강사님
    // Simple JUnit4
    // 어노테이션(Annotation) ; 주석
    // Comment : 비공식적이고 임시적이다.
    // 정의 : 자바의 각 요소(클래스, 메소드, 필드, 매개변수) 가 가질 수 있는
    //        주석 리소스
    
    // 목적
    // 1. 컴파일러에게 추가적인 정보 전달
    // 2. 컴파일 할 때와 설치시의 작업 지정
    // 3. 실행할 때 별도의 처리를 수행
    
    // 기본 어노테이션
    // 1. @Override
    // 2. @Deprecated
    // 3. @SuppressWarnings
    
    // 1. 어노테이션을 만드는 방법.
    
    import java.lang.annotation.*;
    import java.lang.reflect.Method;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @interface Test {
      boolean enabled() default true;
    }
    
    public class Example7 {
      @Test
      public void testA() {
        System.out.println("testA : do something");
      }
    
      @Test(enabled = true)
      public void testB() {
        System.out.println("testB : do something");
        throw new RuntimeException("failed to run");
      }
    
      @Test(enabled = true)
      public void testC() {
        System.out.println("testC : do something");
      }
    
      public static void main(String[] args) {
        System.out.println("Testing....");
    
        int count = 0;
        int passed = 0;
        int failed = 0;
        int ignore = 0;
    
        Class obj = Example7.class;
        for (Method method : obj.getDeclaredMethods()) {
    
          if (method.isAnnotationPresent(Test.class)) {
            ++count;
            Annotation annotation = method.getAnnotation(Test.class);
            Test test = (Test) annotation;
    
            if (test.enabled()) {
              try {
                method.invoke(obj.newInstance());
                ++passed;
                System.out.printf("Test '%s' - passed\n", method.getName());
              } catch (Throwable e) {
                ++failed;
                System.out.printf("Test '%s' - failed : %s\n", method.getName(),
                    e.getCause());
              }
            } else {
              ++ignore;
              System.out.printf("Test '%s' ignored\n", method.getName());
            }
          }
        } // for
    
        System.out.printf("Result - Total:%d, Passed:%d, Failed:%d, Ignore:%d\n",
            count, passed, failed, ignore);
      }
    
    }
    




'Programing > Java' 카테고리의 다른 글

Effective Java - Exception  (0) 2016.03.09
Effective Java - Reflection  (0) 2016.03.08
Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
  • 1_Class 정보
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // Reflection(Introspection)
    // 개념 : Class 이름만으로도 클래스의 정보(필드, 메소드)를 찾거나
    //       객체를 생성할 수 있는 기능.
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    public class Example1 {
      public static void main(String[] args) throws Exception {
        // 1. Class 얻기
        // a. 객체를 통해서 얻는 방법.
        // Class personClass = new Person().getClass();
    
        // b. Class type으로 얻어내는 방법.
        // Class personClass = Person.class;
    
        // c. 문자열로 얻어내는 방법.
        Class personClass = Class.forName("kr.co.ioacademy.Person"); // 패키지 정보까지 정확히 넣어야된다.
    
        // 2. Class 이름
        System.out.println(personClass.getName());       // kr.co.ioacademy.Person
        System.out.println(personClass.getSimpleName()); // Person
        System.out.println(personClass.getCanonicalName());
    
        int mods = personClass.getModifiers();
    
        // 3. Class 속성
        System.out.println("public : " + Modifier.isPublic(mods));
        System.out.println("final : " + Modifier.isFinal(mods));
        System.out.println("abstract : " + Modifier.isAbstract(mods));
    
        // 4. Method 속성
        Method[] methods = personClass.getMethods();
        for (Method m : methods) {
          System.out.println(m.getName());
          System.out.println(m.getParameterCount());
          Class[] params = m.getParameterTypes();
          for (Class p : params) {
            System.out.println("\t" + p.getName());
          }
        }
      }
    }
    
    final class Person {
      private String name;
      private int age;
    
      public Person() {
        this.name = "";
        this.age = 0;
      }
    
      public Person(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      @Override
      public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
      }
    }
    

  • 2_객체 read / write
    : public field vs. private field
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.lang.reflect.Field;
    
    // 객체의 필드를 읽거나 쓰는 방법.
    public class Example2 {
      public static void main(String[] args) throws Exception {
        Point point = new Point();
    
        Class pointClass = point.getClass();
    
        // public field read / write
        Field xField = pointClass.getField("x");
        Field yField = pointClass.getField("y");
    
        System.out.println(xField.get(point) + ", " + yField.get(point));
    
        xField.setInt(point, 10);
        // xField.set(point, 10);
        yField.set(point, 20);
        System.out.println(xField.get(point) + ", " + yField.get(point));
    
        Field[] fields = pointClass.getDeclaredFields();
        for (Field f : fields)
          System.out.println(f.getName());
    
    
        // private field read / write
        Field zField = pointClass.getDeclaredField("z");
        zField.setAccessible(true);
        zField.set(point, 100);
    
        System.out.println(zField.get(point));
      }
    }
    
    class Point {
      public int x;
      public int y;
    
      private int z;
      public int z() {
        return z;
      }
    }
    


  • 3_객체 동적 생성
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.lang.reflect.Constructor;
    
    // Reflection 을 통해서 객체를 생성하는 방법. - 동적 생성
    // Spring - DI(Dependency Injection)
    // iOS
    public class Example3 {
      public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
    
        // 1. 기본 생성자를 통한 객체 생성
        Person person = (Person) personClass.newInstance();
        System.out.println(person);
    
        // 2. 사용자 정의 생성자를 통한 객체 생성
        Class[] paramTypes = {
          String.class, int.class
        };
    
        // Constructor constructor = personClass.getConstructor(paramTypes);
        Constructor constructor = personClass.getDeclaredConstructor(paramTypes);
        System.out.println(constructor);
    
        Object[] cargs = {
            "Tom", 42
        };
    
        constructor.setAccessible(true);
        Person person2 = (Person) constructor.newInstance(cargs);
        System.out.println(person2);
      }
    }
    
    class Person {
      private String name;
      private int age;
    
      public Person() {
        this.name = "";
        this.age = 0;
      }
    
      private Person(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      @Override
      public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
      }
    }
    

  • 3_Refilection 활용
    - 1_Key-Value Coding

     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.lang.reflect.Field;
    
    // Reflection 활용 1.
    // Key-Value Coding : setter / getter
    // Objective-C 기본으로 제공하고 있는 기능.
    public class Example4 {
      public static void main(String[] args) {
        Person person = new Person();
    
        String input = "name";
        String value = "Tom";
    
        person.setValue(input, value);
        person.setValue("phone", "010-1111-2222");
        person.setValue("age", 200);
    
        System.out.println(person.getValue("name"));
        System.out.println(person.getValue("phone"));
        System.out.println(person.getValue("age"));
    
    
        // 일반적인 getter / setter 사용
        /*  
        if (input.equals("name"))
          person.setName(value);
        else if (input.equals("phone"))
          person.setPhone(value);
        */
      }
    }
    
    class Person {
      private String name;
      private String phone;
      private int age;
    
      public Object getValue(String key) {
        Class clazz = this.getClass();
        Field field;
    
        try {
          field = clazz.getDeclaredField(key);
          return field.get(this);
        } catch (NoSuchFieldException | IllegalAccessException e) {
          e.printStackTrace();
        }
    
        return null;
      }
    
      // Reflection을 사용하지 않고 key-value coding
      // but, field가 많아진다면??
      /*  
      public void setValue(String key, Object value) {
      switch (key) {
        case "name":
          this.name = (String) value;
          break;
        case "phone":
          this.phone = (String) value;
          break;
        case "age":
          this.age = (int) value;
          break;
       }
     }*/
      
      public void setValue(String key, Object value) {
        Class clazz = this.getClass(); // Person.class
        try {
          Field filed = clazz.getDeclaredField(key);
          filed.set(this, value);
        } catch (NoSuchFieldException | IllegalAccessException e) {
          e.printStackTrace();
        }
      }
    
      @Override
      public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", phone='" + phone + '\'' +
            '}';
      }
    }
    

    - 2_팩토리
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import org.hamcrest.Factory;
    
    // Reflection 활용 2.
    // 팩토리
    // 개념 : 객체를 생성하는 객체
    // 장점 : 객체 생성에 관한 코드를 한곳에 모아서 중앙집중적으로 관리하는 것이 가능하다.
    // 단점 : 도형의 종류가 늘어남에 따라 팩토리의 코드는 수정되어야만 한다.
    //        OCP(Open Closed Principle) 를 만족할 수 없다.
    
    abstract class Shape {
    	abstract void print();
    }
    class Rect extends Shape {
    	@Override
    	void print() {
    		System.out.println("Rect");
    	}
    }
    class Circle extends Shape {
    	@Override
    	void print() {
    		System.out.println("Circle");
    	}
    }
    class Triangle extends Shape {
    	@Override
    	void print() {
    		System.out.println("Triangle");
    	}
    }
    
    class ShapeFactory {
      public Shape createShape(String name) {
        Class clazz = null;
        try {
          clazz = Class.forName(name);
          return (Shape) clazz.newInstance();
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
          e.printStackTrace();
        }
    
        return null;
      }
    
    //  public Shape createShape(String name) {
    //    if (name.equals("Rect"))
    //      return new Rect();
    //    else if (name.equals("Circle"))
    //      return new Circle();
    //    else if (name.equals("Triangle"))
    //      return new Triangle();
    //
    //    return null;
    //  }
    }
    
    public class Example5 {
    	public static void main(String args[]) {
    		ShapeFactory factory = new ShapeFactory();
    		Object r = factory.createShape("kr.co.ioacademy.Rect");
    		Object c = factory.createShape("kr.co.ioacademy.Circle");
    		Object t = factory.createShape("kr.co.ioacademy.Triangle");
    		
    		if(r instanceof Rect)
    			((Rect)r).print();
    		if(c instanceof Circle)
    			((Circle)c).print();
    		if(t instanceof Triangle)
    			((Triangle)t).print();
    	}
    }
    

  • 4_Reflection 성능 고찰

    package kr.co.ioacademy; //iocademy 윤찬식 강사님 // Reflection 성능. // 1. Refelction 은 염려할만큼의 성능 저하는 없다. // 2. 잘 설계된 Reflection은 객체 지향의 철학을 어긋나지 않으면서, // 더 좋은 코드를 만들어 낼 수 있다. public class Example6 { public static void doRegular() throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { Point p = new Point(); p.print(); } System.out.println(System.currentTimeMillis() - start); } public static void doReflection() throws Exception { long start = System.currentTimeMillis(); Class clazz = Class.forName("kr.co.ioacademy.Point"); for (int i = 0; i < 1000000; i++) { Point p = (Point) clazz.newInstance(); p.print(); } System.out.println(System.currentTimeMillis() - start); } public static void main(String[] args) throws Exception { doRegular(); doReflection(); } } class Point { private int x; private int y; public void print() { } }



  • Reflection에 대한 오해와 진실
    (출처: https://kmongcom.wordpress.com/2014/03/15/%EC%9E%90%EB%B0%94-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%97%90-%EB%8C%80%ED%95%9C-%EC%98%A4%ED%95%B4%EC%99%80-%EC%A7%84%EC%8B%A4/)

    사용처

    현장에서 자바 개발자들이 Reflection을 직접 사용하는 것은 극히 드문 일이다. 그것은 Reflection이 적용될 수 있고 또한 적용되어야 할 곳은 라이브러리 클래스, 공통 컴포넌트 클래스, 그리고 프레임워크와 같이 Reflection을 통해 얻는 이득(재사용성, 확장성, 생산성, 유연성 등)이 극대화될 수 있는 곳이어야 하기 때문이다.

    1. Java Serialization

     
    객체를 직렬화(Serialization) 해야 할 경우 Serializable 인터페이스를 구현한다. 그리고, 직렬화된 객체를 읽기 위해서는 java.io.ObjectInputStream 클래스의 readObject() 메서드를 이용한다. 필요에 따라 Serializable 인터페이스를 구현한 클래스가 readObject() 메서드를 구현할 수도 있다. 이때, java.io.ObjectInputStream 클래스의 readObject() 메서드는 내부적으로 Reflection을 이용하여 직렬화된 객체의 readObject() 메서드를 호출한다.

    a4

    그림 4. Reflection을 사용한 Serialization

    2. Apache Commons BeanUtils library

    Struts 프레임워크를 적용한 프로젝트에서 개발한 경험이 있다면, Apache Commons 프로젝트의 BeanUtils 라이브러리 사용을 고민해 본 경험 또한 있을 것이다. Struts 프레임워크는 HttpRequest 객체의 파라미터를 이용하여 ActionForm 클래스의 객체를 생성한다. 이 ActionForm 클래스의 객체를 생성하는 곳에서도 Reflection이 적용되었다.

    Struts를 이용할 경우, 가장 성가신 부분은 ActionForm 클래스의 객체를 대응하는 VO 클래스의 객체로 변환하는 작업이다. 이 작업은 서비스 레이어를 Struts에 종속되지 않게 하기 위해 또는, 레이어 분리를 위해 반드시 수행되어야 한다. 만일 지금까지 ActionForm 객체를 서비스 레이어로 바로 넘겼다면 다시 한번 생각해 보라. VO 클래스 사용에 따른 레이어의 분리와, VO를 사용하지 않음으로써 얻는 개발 생산성 증가, 둘 중 어느 한가지를 택한 것인지.

     이때, 사용할 수 있는 것이 Commons BeanUtils 라이브러리이다. Beanutils.copyProperties(Object dest, Object orig)를 이용하여 간단히 ActionForm 객체를 VO 객체로 변환할 수 있다. 규칙은 ActionForm 클래스의 인스턴스 변수명과 VO 클래스의 인스턴수 변수명이 같아야 한다는 것이다. 이 규칙을 따른다면, Reflection의 마술이 여러분을 위한 모든 작업을 수행해 줄 것이다.

    이슈

    1. Reflection을 사용한 코드는 느리다

    개발자들 사이에 공공연히 진실로 받아들여지는 이 말은 사실이 아니다. 적절히 사용한 Reflection은 오히려 성능을 향상시킬 수 있으며, 또한 많은 이득을 제공한다. 뿐만 아니라, 성능만을 고려한 구현이 객체 지향의 설계 원칙들을 역행한다면, 오히려 이는 더욱 나쁜 결과를 낳게 된다.

    2. Reflection을 이용하여 개발한 프로그램은 에러가 발생하기 쉽고 디버깅이 어렵다

    Reflection은 컴파일 시 타입 체킹을 할 수 없다. 따라서, 런타임시 잘못된 파라미터로 인해 런타임 에러가 발생하기 쉽다. 이는 사실이다. 하지만, 적절히 사용된 런타임 에러 메시지를 이용해 충분히 디버깅이 쉬운 환경으로 만들 수 있다.

    3. Reflection을 사용한 코드는 복잡하다

    Reflection을 사용한 코드는 일반적인 객체 생성, 메서드 호출 코드에 비교하면 복잡한 것이 사실이다. 하지만 클래스의 타입을 비교하여 객체를 생성하는 코드의 경우, 대량의 if/else문을 사용하는 것보다 Reflection을 이용하여 재사용 가능한 컴포넌트로 만든다면, 오히려 코드를 단순화한다.

    4. 성능(Performance) vs. 유연성(Flexibility)

    앞서 말한 바와 같이 “Reflection을 사용한 코드는 느리다”는 사실은 사실이 아니다. 이 말은 Reflection을 사용할 경우 성능이 떨어지지 않는다라는 얘기가 아니다. 오히려, 성능이 떨어진다는 결과가 다수 존재한다. 아래는 Dennis Sosnoski(5. Java Programming dynamics, Part 2: Introducing reflection)가 측정한 Reflection에 관한 성능 결과이다. 그림에서 알 수 있듯이, Reflection을 사용할 경우, 직접(Direct) 또는 참조(Reference)의 경우에 비해 2 ~ 4배 정도 느리다.


    a5

    그림 5. 필드 변수 Access 시간

    a6

    그림 6. 메서드 호출 시간

    이 결과를 통해 알 수 있는 사실은 “Reflection에 따른 성능 저하”가 아니라 “성능 측정 결과, Reflection을 사용한 
    지금 이 경우에는 성능이 저하되는 것을 검증했다”라는 것이다. 최적화 또는 성능 개선(Optimization)시 유의해야 할 점은 반드시 최적화 이전과 이후의 성능을 측정하여, 성능개선이 가시적으로 보일 때에만 적용해야 한다는 것이다. 만일 최적화가 필요하다고 느낀다면, 아래 규칙을 따르라.

    a7

    그림 7. 최적화 규칙

    Reflection과 관련된 성능에 관한 논쟁은 오해에서 비롯된 것이다. 이것은 JDK 초기 버전(1.3.0 이전 버전)의 경우 Reflection의 성능이 현저히 떨어졌다. 하지만 이후의 JDK 버전에서는, Reflection의 중요성을 인식한 Sun의 지속적인 노력으로 성능이 개선되고 있으며, 앞으로도 개선의 여지가 남아 있다. 뿐만 아니라 잘 적용한 Reflection은 많은 이득을 제공한다.

    Reflection을 통해 얻을 수 있는 가장 큰 이득은 시스템 유연성(Flexibility)이다. “그림 3″에서 보는 바와 같이 Mouse 컨트롤러는 미래의 어떤 마우스와도 동작할 수 있다. 이처럼 성능보다 유연성이 더 중요한 상황이 많다

    물론 Reflection 적용에 따른 가시적인 성능 저하를 확신한다면, 다른 대안을 생각해 볼 수 있다. 대안에는, Reflection 대신 interface를 통한 메서드 호출, 코드 자동 생성(Code Generation), 또는 최악의 경우 하드 코딩이 있다.

    5. Compile vs. Run-time Type Checking

     자바의 경우 컴파일 단계에 강력한 Type Checking을 지원한다. 아래와 같이 두 개의 클래스가 틀릴 경우, 컴파일이 에러가 발생한다.

    a8

    그림 8. 컴파일 에러 예

    Reflection은 실제 클래스 없이 클래스의 이름 또는 메서드의 이름만을 이용한다. 따라서, 아래와 같이, 개발 단계 또는 컴파일 단계에는 Type Checking을 하지 않는다.

    a9

    그림 9. Checked Exception 예

    대신, 실행시 발생할 수 있는 Exception을 처리하기 위한 try/catch문을 추가해야 한다.

    자바가 제공하는 Exception의 종류에는 Checked Exception, Run-time Exception 그리고 error가 있다. 이중 Checked Exception의 경우, 위와 같이 컴파일 단계에 catch하거나 throw해야만 컴파일 오류가 발생하지 않는다. 이는 Checked Exception은 예외 상황에서 프로그램적으로 복구할 수 있는 방법이 존재하는 경우를 위한 Exception이기 때문이다.

    하지만 Reflection 사용에 따른 Exception으로부터 복구할 수 있는 상황이란 거의 없다. 예를 들어, 위의 Class Not Found Exception이 발생한다면, 이는 클라이언트가 잘못된 클래스 명을 넘겨주었거나 또는 해당 클래스가 없을 2가지 경우다. 이는 모두 프로그램 에러 상황으로 오히려 Run-time Exception에 가깝다.

     결국, Reflection을 사용함으로써, 개발단계에서 조기에 발견될 수도 있었을 프로그램 에러들이 런타임시에 발생하게 되는 것이다. 이처럼 에러 상황이 늦게 발견하면 디버깅이 어려워지게 된다. 이와 같은 경우를 위해서, 런타임시 디버깅을 위해 상세한 에러 메시징 기능을 포함하는 것이 좋다.

    Drug heals the pain, Overdose kills the gain

    Reflection의 사용에 관한 사실들은 사실 사실이 아니다. 성능, 디버깅, 그리고 복잡성과 관련된 내용들은 잘못 사용된 예에서 파생한 오해들이다.

    Reflection은 염려할 만큼의 성능저하를 가져오지 않는다. 대량의 if/else문이나 switch문 대신, 잘 설계된 Reflection은 객체지향 철학을 어기지 않으면서도 더 좋은 성능을 발휘할 수도 있다.

    또한 디버깅이 어려운 것은 컴파일 단계에 처리할 수 있는 오류들이 실행 단계에 발생하기 때문이 아니다. 더 근본적인 이유는 Reflection을 사용할 경우에 발생할 런타임 에러 메시지를 최대한 상세하게 그리고 친절하게 표시하도록 Exception 전략을 설계하지 못했기 때문이다. 잘 설계된 Exception 처리 전략은 Reflection 뿐만 아니라 시스템의 전체적인 디버깅을 쉽게 만든다.

    Reflection의 복잡성은 사실 개발자 개개인의 초점에 맞추었을 때의 얘기다. 하지만, Reflection의 사용은 개발자의 관점이 아닌 아키텍트 중심으로 설계되어야 한다. Reflection이 가장 유용한 곳이 바로 시스템의 아키텍처를 이루는 컴포넌트들이기 때문이다. 오히려 잘 설계된 Reflection이 제공하는 서비스는 개발을 더욱 단순화 한다.

    하지만 지나친 사용은 화를 부를 수 있다. Reflection을 적절히 사용했고 많은 이득이 따른다 하더라도, 더 간단한 해결책이 존재한다면, Reflection을 사용하지 말 것을 권한다. 단순한 해결책은 언제 어느 경우에나 최상의 선택이다.

    Reflection의 사용은 양날의 검과 같다. 잘 사용한다면, 이름을 불러주었을 때, 여러분의 꽃이 되어 줄 것이다.


'Programing > Java' 카테고리의 다른 글

Effective Java - Exception  (0) 2016.03.09
Effective Java - Annotation  (0) 2016.03.08
Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
  • Atomic
    : 원자적 연산은 '원자적인'방법 즉, 중간에 어떠한 방해도 받지 않고 어셈블리어 명령어 하나로 실행할수 있다. 
    : 메모리에 접근하지 않거나 한번 접근하는 어셈블리어 명령은 원자적이다.
    : inc나 dec같이 메모리에서 데이터를 읽고, 이를 갱신하고, 갱신한 값을 메모리에 다시 쓰는 읽기/수정/쓰기 어셈블리어 명령은 읽기와 쓰기 사이에 다른 프로세서가 메모리버스를 점유하지 않는 한 원자적이다. 유니프로세서 시스템에서는 같은 프로세서에서 메모리 접근이 일어나므로 메모리버스를 훔치는일은 발생하지 않는다.

    ∴ 윈도우 프로그램에서 동기화
     : 커널 모드 동기화 vs. 유저모드 동기화
     : 유저모드는 Atomic Operation 과 CRITICAL_SECTION 으로 구분
     : 커널 모드는 세마포어와 뮤텍스로 구분된다. 뮤텍스(두 프로세스간)는 세마포어 기법(멀티 프로세스간)의 일부분으로 포함되는 개념이다.

    : java 1.5 부터 java.util.concurrent.atomic에 Atomic 관련 타입들 존재
    : 속도는 perf3 > perf1 > perf2
    : Atomic이 무조건 좋다는 법은 없다. -> 적절하게 사용하면 된다.

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // 1 억 만들기
    
    import org.junit.Test;
    
    import java.util.concurrent.atomic.AtomicLong;
    
    // value += 2; -> Atomic 하지 않다.
    // 1. value 를 메모리부터 로드
    // 2. 2를 더하고
    // 3. 메모리에 저장
    
    public class Example5 {
        private static final int THREAD_COUNT = 2;
        // 1. Atomic 사용
        private static AtomicLong value1 = new AtomicLong(0);
        // private static volatile long value = 0;
    
        private final static Runnable TASK = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000000 / THREAD_COUNT; i++)
                    value1.addAndGet(2);
                    // value += 2;
            }
        };
    
        @Test 
        public void perf1() throws InterruptedException {
            Thread[] threads = new Thread[THREAD_COUNT];
            for (int i = 0; i < THREAD_COUNT; i++)
                threads[i] = new Thread(TASK);
    
            for (Thread t : threads)
                t.start();
    
            boolean alive = true;
            while (alive) {
                for (Thread t : threads) {
                    t.join(10);
                    if (alive = t.isAlive())
                        break;
                }
            }
    
            System.out.println("value : " + value1.get());
        }
        
        // 2. volatile 사용 - for문 연산 자체에 동기화
        private static volatile long value2 = 0;
        private final static Runnable TASK2 = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000000 / THREAD_COUNT; i++)
                    synchronized (Example5.class) {
                        value2 += 2;
                    }
            }
        };
        @Test
        public void perf2() throws InterruptedException {
            Thread[] threads = new Thread[THREAD_COUNT];
            for (int i = 0; i < THREAD_COUNT; i++)
                threads[i] = new Thread(TASK2);
    
            for (Thread t : threads)
                t.start();
    
            boolean alive = true;
            while (alive) {
                for (Thread t : threads) {
                    t.join(10);
                    if (alive = t.isAlive())
                        break;
                }
            }
    
            System.out.println("value : " + value2);
        }
    
        // 3. volatile 사용 - for문 연산 종료 후 동기화
        private static volatile long value3 = 0;
        private final static Runnable TASK3 = new Runnable() {
            @Override
            public void run() {
                long local_sum = 0;
                for (int i = 0; i < 50000000 / THREAD_COUNT; i++)
                    local_sum += 2;
    
                synchronized (Example5.class) {
                    value3 += local_sum;
                }
            }
        };
        @Test
        public void perf3() throws InterruptedException {
            Thread[] threads = new Thread[THREAD_COUNT];
            for (int i = 0; i < THREAD_COUNT; i++)
                threads[i] = new Thread(TASK3);
    
            for (Thread t : threads)
                t.start();
    
            boolean alive = true;
            while (alive) {
                for (Thread t : threads) {
                    t.join(10);
                    if (alive = t.isAlive())
                        break;
                }
            }
    
            System.out.println("value : " + value3);
        }
    }
    

  • 가짜 공유 (False Sharing)
    멀티 코어 CPU에서 발생할 수 있는 문제다. 멀티 코어 CPU에서는 데이터를 word 단위로 읽어오는 대신 메모리 I/O 효율성을 위해서 cache-line로 읽어오는데, 이때 문제가 생길 수 있다. 두개의 메모리 연산이 동일한 캐쉬라인에서 실행될 경우, CPU<->Memory 버스 사이에서 하드웨어적인 락이 걸리는데, 이때 하드웨어적인 병목현상이 발생한다.
    : 멀티코어에서는 A스레드와 B스레드에서 인접 메모리를 접근할때, 캐시에 있던 내용을 메모리에 반영하려 시도. 인접 메모리를 읽고 있는 상태이기에 병행 수행시 데이터의 유효성을 조금이라도 높이기 위해 메모리에 반영하는 과정에서 속도 저하가 발생하는 것.
    캐시 라인이라 함은 지역성에 근거해 인접한 데이터를 미리 읽어옴으로써 속도향상을 노리는 것

    지역성(locality)은 아래 추정에 근거합니다.
    1. 지금 읽힌 데이터는 이후에도 자주 사용될 가능성이 높다.
    2. 지금 읽힌 데이터와 인접한 데이터는 이어서 사용될 가능성이 높다.

    패딩(padding)를 통한 해결 -> 메모리를 손해보더라도 속도에서 이득을 보자
    : p1, p2, p3, p4, p5, p6(64byte)를 선언하여 캐쉬라인에 대한 패딩 확보 (annotation으로도 존재한다.)

    public final class X {
        public volatile int f1;
        public volatile int f2;
    }
    




    참고 : http://www.smallake.kr/?p=2271, http://elky.tistory.com/318, http://daniel.mitterdorfer.name/articles/2014/false-sharing/

    package kr.co.ioacademy; //iocademy 윤찬식 강사님 import org.junit.Test; import sun.misc.Contended; class C { public final static int NUM_THREADS = 8; public final static long ITERATIONS = 50L * 1000L * 1000L; } // 가짜 공유 문제(False Sharing) // 개념 : 두 스레드간의 공유가 발생하지 않음에도 캐시라인의 공유에 의해서 // 서로의 캐시를 무효화함으로 성능이 저하되는 문제. public class Example6 { @Test public void runTest() throws InterruptedException { Thread[] threads = new Thread[C.NUM_THREADS]; for (int i = 0; i < threads.length; i++) { threads[i] = new Task(i); } for (Thread t : threads) { t.start(); } for (Thread t : threads) { t.join(); } } } class Task extends Thread { static class Long { public volatile long value = 0L; public long p1, p2, p3, p4, p5, p6; // 64byte padding } private static Long[] longs = new Long[C.NUM_THREADS]; static { for (int i = 0; i < longs.length; i++) longs[i] = new Long(); } private int index; public Task(int index) { this.index = index; } @Override public void run() { long i = C.ITERATIONS + 1; while (0 != --i) { longs[index].value = i; } } }


'Programing > Java' 카테고리의 다른 글

Effective Java - Annotation  (0) 2016.03.08
Effective Java - Reflection  (0) 2016.03.08
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
Effective Java - 객체 비교, 복제  (0) 2016.03.04
  • Thread 설계
    - Step 1. Single Thread Model
     AutoCloseable Interface

     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    // C99, C11(thread) - Android M
    // C++11/14
    // Java 8, 9
    
    public class Example3 {
        /*
       private static void handleRequest(Socket connection) {
          OutputStream os = null;
          try {
              os = connection.getOutputStream();
              os.write("Hello World".getBytes());
              os.flush();
          } catch (IOException e) {
              e.printStackTrace();
          } finally {
              if (os != null)
                  try {
                      os.close();
                  } catch (IOException e) {
    
                  }
          }
      }
      */
    
        // Try with Resource : Java 7 - (C# using block)
        // 1. Connection, Socket, Stream 비 메모리 자원을 해지하는
        //   명시적인 종료 메소드를 호출해야 하는 것은 코드를 어지럽힌다.
        // 2. try 블록에 존재하는 자원을 자동으로 회수해준다.
        // 3. AutoCloseable Interface
        private static void handleRequest(Socket connection) {
            try (OutputStream os = connection.getOutputStream();
                 InputStream is = connection.getInputStream()) {
                os.write("Hello World".getBytes());
                os.flush();
            } catch (IOException e) {
            }
        }
    
        
        // Step 1. Single Thread Model
        // 1) 한번에 하나의 요청을 처리하는 것이 가능하다.
        // 2) 단일 스레드에서 IO 작업 등으로 대기하는 경우, 하드웨어 자원을
        //    효과적으로 사용할 수 없다.
        public static void main(String[] args) throws IOException {
            ServerSocket socket = new ServerSocket(8080);
    
            while (true) {
                Socket connection = socket.accept();
                handleRequest(connection);
            }
        }
    

    - Step 2. Thread per Connection Model
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    // C99, C11(thread) - Android M
    // C++11/14
    // Java 8, 9
    
    public class Example3 {
        // Try with Resource : Java 7 - (C# using block)
        // 1. Connection, Socket, Stream 비 메모리 자원을 해지하는
        //   명시적인 종료 메소드를 호출해야 하는 것은 코드를 어지럽힌다.
        // 2. try 블록에 존재하는 자원을 자동으로 회수해준다.
        // 3. AutoCloseable Interface
        private static void handleRequest(Socket connection) {
            try (OutputStream os = connection.getOutputStream();
                 InputStream is = connection.getInputStream()) {
                os.write("Hello World".getBytes());
                os.flush();
            } catch (IOException e) {
            }
        }
        
    
        
        // Step 2. Thread per Connection Model
        // 순차적인 실행 방법보다 훨씬 더 많은 작업을 수행하는 것이 가능하다.
    
        // 문제점
        // 1. 엄청나게 많은 스레드가 생성된다.
        // 2. Thread 를 만들고 제거하는 작업에도 자원이 소모된다.
        // : 클라이언트의 요청이 작은 단위로 일어나는 경우에는 더욱더 문제가 심하다.
        // 3. 자원 낭비가 심하다.
        //  1) 실행 중인 스레드는 메모리를 많이 소모한다.
        //     Java의 스택은 두 개이다.(Java, Native)
        //  2) 프로세서의 개수 보다 많은 스레드가 생성되면 대부분의 스레드는
        //     대기 상태에 있다.
        // 4. 컨텍스트 스위칭의 비용이 크다.
        // 5. OS 에서 생성할 수 있는 스레드의 개수는 제한되어 있다.
        public static void main(String[] args) throws IOException {
            ServerSocket socket = new ServerSocket(8080);
            while (true) {
                final Socket connection = socket.accept();
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        handleRequest(connection);
                    }
                };
    
                new Thread(task).start();
            }
        }
    

    - Step 3. Pool-based Model
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    // C99, C11(thread) - Android M
    // C++11/14
    // Java 8, 9
    
    public class Example3 {
        // Try with Resource : Java 7 - (C# using block)
        // 1. Connection, Socket, Stream 비 메모리 자원을 해지하는
        //   명시적인 종료 메소드를 호출해야 하는 것은 코드를 어지럽힌다.
        // 2. try 블록에 존재하는 자원을 자동으로 회수해준다.
        // 3. AutoCloseable Interface
        private static void handleRequest(Socket connection) {
            try (OutputStream os = connection.getOutputStream();
                 InputStream is = connection.getInputStream()) {
                os.write("Hello World".getBytes());
                os.flush();
            } catch (IOException e) {
            }
        }
        
        // Step 3. Pool-based Model
        // 순차적으로 문제를 해결하는 방법은 응답 속도와 전체적인 성능이 떨어지는
        // 문제점이 있고 작업별로 스레드를 만들어 내는 방법은 자원 관리 측면에서
        // 문제점이 있다
    
        // 해결책)
        // 1) 스레드의 생성을 제한해야 한다 -> Thread pool
        // 2) 생산자 & 소비자 모델
    
        public static final int NTHREADS = 100;
        public static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
        // 1) newFixedThreadPool
        // 작업이 등록되면 제한된 개수까지 스레드를 생성해서 해당 작업을 처리한다.
        // 생성 이후에 더 이상 생성하지 않고 스레드 수를 유지한다.
    
        // 2) newCachedThreadPool
        // 스레드 수가 처리할 작업보다 많아서 쉬는 스레드가 발생하면,
        // 스레드를 종료한다.
    
        // 3) newSingleThreadPool
        // 단일 스레드로 처리한다. 등록된 작업은 다양한 정책에 의해서 수행될 수 있다.
        // (LIFO, FIFO, 우선순위)
        public static void main(String[] args) throws IOException {
            ServerSocket socket = new ServerSocket(8080);
    
            while (true) {
                Socket connection = socket.accept();
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        handleRequest(connection);
                    }
                };
    
                exec.execute(task);
            }
    
        }
    }
    

  • 스레드의 중단 및 종료 처리
    - 1_동기화
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // 스레드의 중단 및 종료 처리
    // 1. 스레드를 만들고 시작하는 것은 쉬운 일이다.
    // 2. 스레드를 안전하고 빠르게 멈추게 하는 것은 어렵다.
    // 3. 자바의 스레드를 멈추는 기능이 폐기되었다.
    //   Thread.stop() / Thread.suspend()
    
    // 4. 스레드를 강제로 종료하면 공유되어 있는 여러 가지 상태가
    //    망가질 수 있다.
    // 5. 스레드를 멈춰달라는 요청이 오면 진행 중이던 모든 작업을 정리한 후
    //    스스로 종료하도록 만들어야 한다.
    
    
    import java.util.concurrent.TimeUnit;
    
    
    /*public class Example4 {
        private static boolean stopRequested = false;
        // 문제점 : main 스레드가 변경한 stopRequested 의 새로운 값을 background
        //        thread 가 관측할 수 없다.
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    long i = 0;
                    while (!stopRequested) {
                        i++;
                        //System.out.println("value - "  + i);
                    }
                }
            });
    
            backgroundThread.start();
    
            TimeUnit.SECONDS.sleep(3);
            stopRequested = true;
        }
    
    }
    */
    
    // 문제점 : main 스레드가 변경한 stopRequested 의 새로운 값을 background
    //        thread 가 관측할 수 없다.
    
    // 해결책 1. 동기화(synchronization)
    // 1) 상호 배제 : 다른 스레드가 변경 중인 객체의 상태를 관측할 수 없도록 해준다. - 락
    // 2) 동기화는 동기화 메소드 또는 동기화 블록에 의해 진입한 스레드가 동일한 락의 보호 아래
    //    이루어진 모든 변경을 관측할 수 있다.
    
    // 문제점 : 순환문의 각 단계마다 동기화를 실행하는 비용이 크다.
    
    
    public class Example4 {
        private static synchronized void stop() { stopRequested = true; }
        private static synchronized boolean isStopRequested() {
            return stopRequested;
        }
    
        private static boolean stopRequested = false;
    
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    long i = 0;
                    while (!isStopRequested()) {
                        i++;
                        System.out.println("value - "  + i);
                    }
                }
            });
    
            backgroundThread.start();
    
            TimeUnit.SECONDS.sleep(3);
            stop();
            stopRequested = true;
        }
    }
    

  • - 2_cv 제한자 (const-volatile)
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // 스레드의 중단 및 종료 처리
    // 1. 스레드를 만들고 시작하는 것은 쉬운 일이다.
    // 2. 스레드를 안전하고 빠르게 멈추게 하는 것은 어렵다.
    // 3. 자바의 스레드를 멈추는 기능이 폐기되었다.
    //   Thread.stop() / Thread.suspend()
    
    // 4. 스레드를 강제로 종료하면 공유되어 있는 여러 가지 상태가
    //    망가질 수 있다.
    // 5. 스레드를 멈춰달라는 요청이 오면 진행 중이던 모든 작업을 정리한 후
    //    스스로 종료하도록 만들어야 한다.
    
    import java.util.concurrent.TimeUnit;
    
    // cv 제한자(const-volatile) : C, C++, C# - Debug / [Release]
    
    // 해결책 2. volatile
    // 정의 : 컴파일러가 최적화를 하지 말라는 지시어 (내부에 cache하여 사용하지 않도록 막음.)
    //   멀티 스레드 상에서 공유되는 변수, 하드웨어와 연결된 변수 사용시
    //   되도록 volatile 붙이자.
    // 효과 : 어떤 스레드건 가장 최근에 기록된 값을 읽을 수 있다.
    public class Example4 {
        private static volatile boolean stopRequested = false;
    
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    long i = 0;
                    while (!stopRequested) {
                        i++;
                        System.out.println("value - "  + i);
                    }
                }
            });
    
            backgroundThread.start();
    
            TimeUnit.SECONDS.sleep(3);
            stopRequested = true;
        }
    }
    


'Programing > Java' 카테고리의 다른 글

Effective Java - Reflection  (0) 2016.03.08
Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
Effective Java - 객체 비교, 복제  (0) 2016.03.04
Effective Java - 객체 소멸  (0) 2016.03.04

+ Recent posts