C++


일반 멤버변수 초기화

- C++ 같은 경우는 멤버변수를 선언과 동시에 초기화를 못시키기때문에 (수정: C++11 부터는 가능) 생성자에서 초기화하며 ,
  생성자 내부 말고 ,생성자 초기화리스트에서 생성하는  효율적인 이디엄이있으며, 

- 자바의 경우 멤버번수를 자동 초기화 해주지만 C/C++ 은 그러지 않기때문에  초기화 과정이 필요함.


class A {
    B b = new B();  <-- 에러이다.
}

- 초기화 리스트를 사용하지 않은 경우.

class A
{
      int x;
      int y;
public:
      Point(int a, int b)
      {
            x = a;
            y = b;
      }
};

 - 초기화 리스트를 사용한 경우.

class A
{
      int x;
      int y;
public:
      Point(int a, int b) : x(a), y(b)
      {
      }
};

이 경우는  int x = a; 와 같이 바로 초기화 된다.


const 멤버변수 

const int a = 20;          //에러 발생 

const int i;                   //에러 발생
    

const 는 바로 초기화 해야한다.  이때 멤버변수라 문제가 생기는데 이때 초기화 리스트를 사용할수 있다.
초기화 리스트에서 초기화 안하면 에러발생한다.

#pragma once
class test
{
const  int n;

public:
test(void);
~test(void);

};

test::test(void):n(0)
{
}

정적 멤버변수 

static  int s;                       // ok

static  int s = 10;                //에러 발생 

const static  int s = 10;       //ok



//////////////////////   test.h /////////////////////

#pragma once
class test
{
const  int n;
static int s;
const static  int cs;

public:
test(void);
~test(void);

};


//////////////////////   test.cpp /////////////////////

#include "test.h"

const int test::cs = 10;       // 이렇게 초기화도 가능하다.
int test::s = 10;

test::test(void):n(0)
{
}




자바
 


- 자바같은경우는 ,멤버변수(인스턴스) 변수를  생성자 뿐만 아니라 선언하면서 바로 대입가능.
- 자바 경우는 멤버변수는 자동 초기화 , 지역변수는 자동초기화 안됨. 
- 자바의 경우 

Example 1:

class A {
    B b = new B();  <-- 정상
}

Example 2:

class A {
    B b;

    A() {
         b = new B();  <-- 정상  
    }
}

Exampe 1 과 Example2 방식의  차이점 정리  (선언하자마자 대입 vs 나중에 대입) 


  • 둘간의 근본적인 차이는 없다. (둘다 컴파일시 생성자에서 초기화 된다)
  • 첫번째 방법이 좀 더 읽기 쉽다.
  • 첫번째 방법으로는 예외처리가 쉽지 않다. ( 선언 아래에 {} 를 추가하여 할수있다)
  • 첫번째 방법은 너무 이른 할당을 하게 한다.
  • 두번째 방법은 생성자가 여러개일때 이곳 저곳에 해야한다. 까먹을수도.. (init() 함수이용)
  • 첫번째 방법으로는  DI 를 '쉽게' 하기 어렵다. 
  • 상황에 맞춰서 쓰도록~


'Java' 카테고리의 다른 글

자바8 Streams API 를 다룰때 실수하기 쉬운것 10가지  (0) 2016.05.22
자바로 MS 문서 다루기  (0) 2015.11.05
자바 EnumBitSet 사용하기  (0) 2015.09.01
자바 enum 정리  (0) 2015.09.01
자바 volatile / C volatile 정리  (0) 2015.09.01

http://claude-martin.ch/enumbitset/  참조 (라이브러리 여기있음)


Project EnumBitSet

This is the project home for EnumBitSet. It's a small project offering more functionality with enum types in Java.

Java 8 is needed to use any of the code!!

Is EnumBitSet the right thing for you?

Do you know this situation: You have created some enum types in Java and now you want to use them but you don't know how to store them in a database? Hibernate and JPA can help you but ORM isn't always the best approach. Maybe you just want to have more control on how the data is stored to the database. 
EnumBitSet can help you working with sets of enum constants and storing those sets in one single database field. Just use the method toLong() if you are sure that there are not more than 64 elements in the enum type. Or use toBigInteger() for enum types of any size. 
This library also offers a more general interface to bit sets that are restricted by a domain (universe). Three implementations exist:

  • EnumBitSet (for enum types, mutable)
  • GeneralDomainBitSet (for all types, mutable)
  • SmallDomainBitSet (for up to 64 elements of any type, immutable)


'Java' 카테고리의 다른 글

자바로 MS 문서 다루기  (0) 2015.11.05
C++ 멤버변수 와 자바 멤버변수 초기화  (0) 2015.09.26
자바 enum 정리  (0) 2015.09.01
자바 volatile / C volatile 정리  (0) 2015.09.01
자바 Concurrent 라이브러리 정리  (0) 2015.08.31

사용법 1  :   클래스의 멤버로 사용 


public enum Currency {PENNY, NICKLE, DIME, QUARTER};

Currency coin = Currency.PENNY;

coin = 1; //compilation error


사용법 2  :   클래스의 멤버로 사용 (값을 지정) 


public enum Currency {PENNY(1), NICKLE(5), DIME(10), QUARTER(25)};


사용법 3  :   Switch 문의 인자로 사용  


Currency usCoin = Currency.DIME;
    switch (usCoin) {
            case PENNY:
                    System.out.println("Penny coin");
                    break;
            case NICKLE:
                    System.out.println("Nickle coin");
                    break;
            case DIME:
                    System.out.println("Dime coin");
                    break;
            case QUARTER:
                    System.out.println("Quarter coin");
    }


사용법 4  :   enum 안에 정의된 상수들은 final 이라, == 으로 비교가능 


Currency usCoin = Currency.DIME;
    if(usCoin == Currency.DIME){
       System.out.println("enum in java can be"+
               "compared using ==");
    }


사용법 5  :   enum 안에 정의된 상수들은 final 이라, == 으로 비교가능 


사용법 6  :  메소드 확장하기 


public enum SizeEnum {


  SMALL("S"), MEDIUM("M"), LARGE("L");

  // Fields

  private String mAbbreviation;

  // Constructor

  private SizeEnum(String abbreviation) {

   mAbbreviation = abbreviation;

  }

   

  // Methods

  public String getAbbreviation() { return mAbbreviation; }

  public void setAbbreviation(String abbreviation) { mAbbreviation = abbreviation; }

 

}


사용법 7  :  Enum 클래스로 사용하기 


public enum FontStyle {
    NORMAL, BOLD, ITALIC, UNDERLINE;
 
    FontStyle() {
    }
}

인라인 클래스로 사용  ( sample.FontStyle.NORMAL  접근) 

public enum SampleClass {
    public enum FontStyle { NORMAL, BOLD, ITALIC, UNDERLINE }
    ...
}


사용법 7  :  EnumSet 사용하기

import java.util.EnumSet;

import java.util.Set; /** * Simple Java Program to demonstrate how to use EnumSet. * It has some interesting use cases and it's specialized collection for * Enumeration types. Using Enum with EnumSet will give you far better * performance than using Enum with HashSet, or LinkedHashSet. * * @author Javin Paul */ public class EnumSetDemo { private enum Color { RED(255, 0, 0), GREEN(0, 255, 0), BLUE(0, 0, 255); private int r; private int g; private int b; private Color(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } public int getR() { return r; } public int getG() { return g; } public int getB() { return b; } } public static void main(String args[]) { // this will draw line in yellow color EnumSet<Color> yellow = EnumSet.of(Color.RED, Color.GREEN); drawLine(yellow); // RED + GREEN + BLUE = WHITE EnumSet<Color> white = EnumSet.of(Color.RED, Color.GREEN, Color.BLUE); drawLine(white); // RED + BLUE = PINK EnumSet<Color> pink = EnumSet.of(Color.RED, Color.BLUE); drawLine(pink); } public static void drawLine(Set<Color> colors) { System.out.println("Requested Colors to draw lines : " + colors); for (Color c : colors) { System.out.println("drawing line in color : " + c); } } } Output: Requested Colors to draw lines : [RED, GREEN] drawing line in color : RED drawing line in color : GREEN Requested Colors to draw lines : [RED, GREEN, BLUE] drawing line in color : RED drawing line in color : GREEN drawing line in color : BLUE Requested Colors to draw lines : [RED, BLUE] drawing line in color : RED drawing line in color : BLUE



사용법 8  :  EnumSet 으로 비트필드 대체하기 

package resolver;

public class IntEnumPatternExample {

    public static final int STYLE_BOLD          = 1 << 0; // 1
    public static final int STYLE_ITALIC        = 1 << 1; // 2
    public static final int STYLE_UNDERLINE     = 1 << 2; // 4
    public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8

    public static void main(String[] args) {
        final IntEnumPatternResolver resolver = new IntEnumPatternResolver();
        resolver.enableAll(STYLE_BOLD, STYLE_ITALIC, STYLE_STRIKETHROUGH, STYLE_UNDERLINE);
        resolver.disable(STYLE_STRIKETHROUGH);
        resolver.toggle(STYLE_UNDERLINE);
        print(resolver);
    }

    private static void print(IntEnumPatternResolver resolver) {
        assert resolver.isEnabled(STYLE_BOLD) == true;
        assert resolver.isEnabled(STYLE_ITALIC) == true;
        assert resolver.isEnabled(STYLE_UNDERLINE) == false;
        assert resolver.isEnabled(STYLE_STRIKETHROUGH) == false;

        System.out.println("STYLE_BOLD: " + resolver.isEnabled(STYLE_BOLD));
        System.out.println("STYLE_ITALIC: " + resolver.isEnabled(STYLE_ITALIC));
        System.out.println("STYLE_UNDERLINE: " + resolver.isEnabled(STYLE_UNDERLINE));
        System.out.println("STYLE_STRIKETHROUGH: " + resolver.isEnabled(STYLE_STRIKETHROUGH));
    }

}
package resolver;

import java.util.EnumSet;

public class EnumPatternExample {

    public enum Style {
        BOLD, ITALIC, UNDERLINE, STRIKETHROUGH
    }

    public static void main(String[] args) {
        final EnumSet<Style> styles = EnumSet.noneOf(Style.class);
        styles.addAll(EnumSet.range(Style.BOLD, Style.STRIKETHROUGH)); // enable all constants
        styles.removeAll(EnumSet.of(Style.UNDERLINE, Style.STRIKETHROUGH)); // disable a couple
        assert EnumSet.of(Style.BOLD, Style.ITALIC).equals(styles); // check set contents are correct
        System.out.println(styles);
    }

}

http://claude-martin.ch/enumbitset/  완전한 EnumBitSet 라이브러리 


사용법 8  :  EnumMap 사용하기 


enum Importance {
Low, Medium, High, Critical
}


EnumMap<Importance, String> enumMap = new EnumMap<>(Importance.class);


enumMap.put(Importance.Low, "=Low");
enumMap.put(Importance.High, "=High");


String value1 = enumMap.get(Importance.Low);
String value2 = enumMap.get(Importance.High);


package program;
import java.util.EnumMap;
public class Program {
    // 1. Create an Enum.
    enum Importance {
Low, Medium, High, Critical
    }
    public static void main(String[] args) {
        // 2. Create an EnumMap.
        EnumMap<Importance, String> enumMap = new EnumMap<>(Importance.class);
        // 3. PUT values into the map.
        enumMap.put(Importance.Low, "=Low");
        enumMap.put(Importance.High, "=High");
        // 4. Get values from the map.
        String value1 = enumMap.get(Importance.Low);
        String value2 = enumMap.get(Importance.High);
        System.out.println(value1);
        System.out.println(value2);
    }
}



Java volatile



- volatile 변수를 읽어 들일 때 CPU 캐시가 아니라 컴퓨터의 메인 메모리로 부터 읽어들입니다.
그리고 volatile 변수를 쓸 때에도(write) CPU 캐시가 아닌 메인 메모리에 기록합니다.

 - non-volatile 변수들은 어느 시점에 Java Virtual Machine(JVM)이 메인 메모리로 부터 데이터를 읽어 CPU 캐시로 읽어 들이거나 혹은 CPU 캐시들에서 메인 메모리로 데이터를 쓰는지(write) 보장해 줄 수 없습니다. 

- 이때 volatile 을 쓰면, 메모리에 있는 최신의 값을 보기때문에 문제의 소지를 없앨가능성을 높힙니다.

- 하지만 모든걸 해결해주진  못합니다.

- 멀티쓰레드에서 하나의 volatile 변수를 접근할때, 하나의 쓰레드가 아직 메모리에 못 썼다면 , 나머지 쓰레드는
  역시 이전값을 가지게 될것이며,  또는 두개의 쓰레드가 동시에 하나의 변수를 가져다가 작업을 하면 마지막에   쓰여진 값이 최종값이 될것입니다. 두 쓰레드간의 하나의 변수에 대한 동기화가 깨진상태가 됩니다.

- race condition 이 발생할 염려가 되는곳이라면 , synchronized으로 보장해주는것이 필요해보입니다.

java.util.concurrent package 의 AtomicLong or AtomicReference 를 사용하세요.  

- (역주: while (상태변수) {  ....  notify()  } 이런 류의 코드의  상태 변수는 반드시 volatile 해주는것이 좋습니다.)

- volatile 만으로 충분한 상황은  하나의 쓰레드는 쓰고/읽기 , 나머지는 읽기만 한다면 락 없어도 가능합니다.

- volatile 키워드는 32 bit 변수에서만 기능을 보장합니다. 64bit 는 실패될수있습니다. 

- volatile 키워드는 성능에는 아주 조금 안좋겠지요. 필요할때만 사용하십시요. 

Java 5의 volatile 키워드는 단순히 변수를 메인 메모리로 부터 읽고 쓰는것 이상을  보장(guarantees) 해
  줍니다. 아래 내용을 읽어보세요 ( 오해의 소지가 많을듯해서  원문을 직접보시는게 나음) 


The Java volatile Happens-Before Guarantee



C/C++  volatile


volatile 키워드는 앞서 살펴본 하드웨어 제어를 포함하여 크게 3가지 경우에 흔히 사용된다.

 

(1) MMIO(Memory-mapped I/O)

(2) 인터럽트 서비스 루틴(Interrupt Service Routine)의 사용

(3) 멀티 쓰레드 환경


세 가지 모두 공통점은 현재 프로그램의 수행 흐름과 상관없이 외부 요인이 변수 값을 변경할 수 있다는 점이다. 인터럽트 서비스 루틴이나 멀티 쓰레드 프로그램의 경우 일반적으로 스택에 할당하는 지역 변수는 공유하지 않으므로, 서로 공유되는 전역 변수의 경우에만 필요에 따라 volatile을 사용하면 된다.


int done = FALSE;

void main()

{

     ...

     while (!done)

     {

         // Wait

     }

     ...

}

 

interrupt void serial_isr(void)

{

     ...

     if (ETX == rxChar)

     {

         done = TRUE;

     }

     ...

} 

serial.c


위 시리얼 통신 예제는 전역 변수로 done을 선언해서 시리얼 통신 종료를 알리는 ETX 문자를 받으면 main 프로그램을 종료시킨다. 문제는 done이 volatile이 아니므로 main 프로그램은 while(!done)을 수행할 때 매번 메모리에서 done을 새로 읽어오지 않는다는 점이다. 따라서 serial_isr() 루틴이 done 플래그를 수정하더라도 main은 이를 모른 채 계속 루프를 돌고 있을 수 있다. done을 volatile로 선언해주면 매번 메모리에서 변수 값을 새로 읽어오므로 이 문제가 해결된다.

인터럽트의 경우와 마찬가지로 멀티 쓰레드 프로그램도 수행 도중에 다른 쓰레드가 전역 변수 값을 임의로 변경할 수 있다. 하지만 컴파일러가 코드를 생성할 때는 다른 쓰레드의 존재 여부를 모르므로 변수 값이 변경되지 않았다면 매번 새롭게 메모리에서 값을 읽어오지 않는다. 따라서 여러 쓰레드가 공유하는 전역 변수라면 volatile로 선언해주거나 명시적으로 락(lock)을 잡아야 한다.

이처럼 레지스터를 재사용하지 않고 반드시 메모리를 참조할 경우 가시성(visibility) 이 보장된다고 말한다. 멀티쓰레드 프로그램이라면 한 쓰레드가 메모리에 쓴 내용이 다른 쓰레드에 보인다는 것을 의미한다.



레퍼런스

http://tutorials.jenkov.com/java-concurrency/volatile.html  <-- 자바 volatile 설명

http://skyul.tistory.com/337   <-- C/C++ volatile 설명 

'Java' 카테고리의 다른 글

자바 EnumBitSet 사용하기  (0) 2015.09.01
자바 enum 정리  (0) 2015.09.01
자바 Concurrent 라이브러리 정리  (0) 2015.08.31
자바 쓰레드 점유율 팁 ( 점유율을 공평하게 만들자)  (0) 2015.08.18
자바 쓰레드 테스트  (0) 2015.08.18


Executors 유틸리티 클래스를 이용하여 각종  쓰레드 풀 생성하기 


ExecutorService =  Executors.newFixedThreadPool(int nThreads)

최대 지정한 개수 만큼의 쓰레드를 가질 수 있는 쓰레드 풀을 생성한다. 실제 생성되는 객체는 ThreadPoolExecutor 객체이다.

항상 일정한 스레드 개수를 유지한다. 스레드가 유휴상태이더라도 제거하지 않고 유지한다.

다만 작업도중 비정상적으로 스레드가 종료하는 경우에는 스레드를 추가로 생성하며, nThreads 개수보다 1개가 더 생길 수

도 있다.


ScheduledExecutorService  =  Executors.newScheduledThreadPool(int corePoolSize)

지정한 개수만큼 쓰레드가 유지되는 스케줄 가능한 쓰레드 풀을 생성한다. 실제 생성되는 객체는

ScheduledThreadPoolExecutor 객체이다.



ExecutorService = Executors.newSingleThreadExecutor()

하나의 쓰레드만 사용하는 ExecutorService를 생성한다.

항상 1개의 스레드만 동작한다. 따라서 스레드가 동작중일 경우 나머지 작업은 모두 큐에서 대기하며, 순서대로 하나씩 실

행된다. 만약 비정상적으로 스레드가 종료되는 경우, 새로 스레드를 생성하고 남은 작업을 계속 한다.


ScheduledExecutorService = Executors. newSingleThreadScheduledExecutor()

하나의 쓰레드만 사용하는 ScheduledExecutorService를 생성한다.일정 시간 이후에 실행되거나 주기적으로 작업을 실행할 

수 있으며, 스레드의 수가 고정되어 있는 형태의 Executor.Timer  클래스의 기능과 유사하다 


ExecutorService = Executors.newCachedThreadPool()

필요할 때 마다 쓰레드를 생성하는 쓰레드 풀을 생성한다. 이미 생성된 쓰레드의 경우 재사용된다.

실제 생성되는 객체는 ThreadPoolExecutor 객체이다. 
스레드 개수에 제한이 없이 필요한 경우 계속 스레드 수가 증가한다.

다만 일정 시간(60초)동안 사용하지 않는(idle) 스레드는 종료된다.

필요없는 스레드를 제거하므로 서버 리소스(memory)는 적게 사용하지만, 스레드 생성과 삭제를 반복하므로 작업 부하가 

불규칙적인 경우 비효율적이다. 



(http://javacan.tistory.com/124  상세설명) 





CopyOnWriteArrayList 

CopyOnWrite 가 말해주는것처럼 read (select) 시는 아무런 동기화 문제가 없기때문에 놔두고 

변경이 일어날경우 객체를 clone 해서 다루자는 전략입니다. 따라서 읽기행위가 많이 일어나는 

곳에서 사용하기 좋습니다. 


BlockingQueue 

보통 생산자 - 소비자 패턴에서 활용되는 큐로 많이 사용된다. 사실 이야기는 이 큐는 멀티쓰레드환경에서 

대표할만한 컬렉션이라는 것이다. 전에 Actor / Akka 문서에 말한 큐같은것들이 대부분 이것으로 이루어져있다. 

소비자가 꺼내어 사용할동안 생산자는 멈춰있고, 생산자가 넣을동안 소비자는 멈춰있어야한다.

서로 쟁탈하면 선반은 망가질것이다.


ConcurrentHashMap

ConcurrentHashMap은 Map의 일부에만 Lock을 걸기때문에 HashTable과 synchronized Map 보다 

효율적인게 특징이다.




Runnable  :  결과값을 리턴하지 않는다 - void run()


Callable     :  결과 값을 리턴한다.  - V call()

 

Future        :  Callable의 리턴값은 실행시키지마자 얻을수있는게 아니라 미래에 얻게된다.
                      그 값을 받을수있는 인터페이스이다.



ThreadLocal  

함수안의 로컬변수는 쓰레드 마다  고유하게 가질수있는것은 알것이다. 그럼 쓰레드마다 고유의 변수를 해당 함수의 안 뿐만아니라, 클래스의 정적멤버등으로 생성하여  각각의  쓰레드가 다른곳에서 사용하고싶을땐?  그때 이것을 사용할수있다. 

ThreadLocal은 한 쓰레드에서 실행되는 코드가 동일한 객체를 사용할 수 있도록 해 주기 때문에 쓰레드와 관련된 코드에서 파라미터를 사용하지 않고 객체를 전파하기 위한 용도로 주로 사용되며, 주요 용도는 다음과 같다.

-사용자 인증정보 전파 - Spring Security에서는 ThreadLocal을 이용해서 사용자 인증 정보를 전파한다.
-트랜잭션 컨텍스트 전파 - 트랜잭션 매니저는 트랜잭션 컨텍스트를 전파하는 데 ThreadLocal을 사용한다.
-쓰레드에 안전해야 하는 데이터 보관

ThreadLocal 사용시 주의 사항

쓰레드 풀 환경에서 ThreadLocal을 사용하는 경우 ThreadLocal 변수에 보관된 데이터의 사용이 끝나면 반드시 해당 데이터를 삭제해 주어야 한다. 그렇지 않을 경우 재사용되는 쓰레드가 올바르지 않은 데이터를 참조할 수 있다.


CountDownLatch 

모든쓰레드(테스크)가 종료되면 , 호출될 필요가 있는곳에 사용된다.  관련  쓰레드들의  이벤트들을 감지 하기위해 

사용한다.


CyclicBarrier

CountDownLatch 와는 반대(?) 로 모든쓰레드가  종료가 아니라 블럭되면 , 호출될 필요가 있는곳에 사용된다.    

다른 쓰레드를 기다리기위해 사용한다고 볼수있다.

하마(HAMA) 라는 분산머신러닝에 사용되는 오픈소스가 있는데, BSP 알고리즘을 사용하는데 BSP 란 

각각의 컴퓨터가 일을 하고, 자신의 일이 끝나면 멈춰있게된다, 모든 컴퓨터들이 멈춰있게되면 ( 각각의 일을 끝마치면) 

서로 커뮤니케이션하는 구조인데,  전체 컴퓨터들의 분산락을 걸어주는것과 비슷한것이다.

분산락은 Zookeeper 라는 오픈소스를 사용한다.


Exchanger 

서로 다른 쓰레드에서 각각의 데이터 (컬렉션을 통채로도) 를 주고 받을수있게한다. 별로 쓸일 없어보인다.

public class ExchangerTest {  
 
    private static final int FULL = 5;
    private static final int COUNT = FULL * 2;
    private static final Random random = new Random();
   
    private static volatile int sum = 0;
    
    private static Exchanger<List<Integer>> exchanger =
        new Exchanger<List<Integer>>();
   
    private static CountDownLatch stopLatch =
        new CountDownLatch(2);
 
    private static List<Integer> initiallyEmptyBuffer;
    private static List<Integer> initiallyFillBuffer;
   
    private static class FillingLoop implements Runnable {
        public void run() {
            List<Integer> currentBuffer = initiallyFillBuffer;
            try {
                for (int i = 0; i < COUNT; i++) {
                    if (currentBuffer == null)
                        break;
                   
                    Integer item = random.nextInt(100);
                    System.out.println("Item Added: " + item);
                    currentBuffer.add(item);
                   
                    if (currentBuffer.size() == FULL) {
                        currentBuffer = exchanger.exchange(currentBuffer);
                    }
                }
            } catch (InterruptedException ex) {
                System.out.println("Bad exchange on filling side");
            }
            stopLatch.countDown();
        }
    }  
 
    private static class EmptyingLoop implements Runnable {
        public void run() {
            List<Integer> currentBuffer = initiallyEmptyBuffer;
            try {
                for (int i = 0; i < COUNT; i++) {
                    if (currentBuffer == null)
                        break;
 
                    if (currentBuffer.isEmpty()) {
                        currentBuffer = exchanger.exchange(currentBuffer);
                    }
                    
                    Integer item = currentBuffer.remove(0);
                    System.out.println("Item Got: " + item);
                    sum += item.intValue();
                   
                }
            } catch (InterruptedException ex) {
                System.out.println("Bad exchange on emptying side");
            }
            stopLatch.countDown();
        }
    }  
 
    public static void main(String args[]) {
        initiallyEmptyBuffer = new ArrayList<Integer>();
        initiallyFillBuffer = new ArrayList<Integer>();
   
        new Thread(new FillingLoop()).start();
        new Thread(new EmptyingLoop()).start();
   
        try {
            stopLatch.await();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("Sum of all items is.... " + sum);
    }
}


FutureTask

모든쓰레드(테스크)가 종료되면 , 호출될 필요가 있는곳에 사용된다. 

public class Preloader {

private final FutureTask<List<String>> task1 
= new FutureTask<List<String>>(new MyCallable());
private final FutureTask<List<String>> task2
= new FutureTask<List<String>>(new MyCallable());
private final FutureTask<List<String>> task3
= new FutureTask<List<String>>(new MyCallable());
private final FutureTask<List<String>> task4
= new FutureTask<List<String>>(new MyCallable());
private final FutureTask<List<String>> task5
= new FutureTask<List<String>>(new MyCallable());
ExecutorService es = Executors.newFixedThreadPool(5);
public void testGO() throws Exception{
es.submit (task1);
Thread.sleep(300);
es.submit (task2);
Thread.sleep(300);
es.submit (task3);
Thread.sleep(300);
es.submit (task4);
Thread.sleep(300);
es.submit (task5);
PrintTest(task2);
PrintTest(task5);
PrintTest(task4);
PrintTest(task3);
PrintTest(task1);
Thread.sleep(20000);
es.shutdown();
} //End Of testGo
private void PrintTest(FutureTask<List<String>> tempTask){
List<String> k = null;
try {
k = tempTask.get();
} catch (InterruptedException e) {
System.out.println("Exception : "+e.getMessage());
} catch (ExecutionException e) {
System.out.println("Exception : "+e.getMessage());
} //End Of try
for(String l : k){
System.out.println(l);
}
} //End Of printTest
    public static void main(String[] args) {

     Preloader preloader = new Preloader();
    
     try {
preloader.testGO();
} catch (Exception e) {
System.out.println(e.getMessage());
} //End Of try
    
    } //End Of main
    
} //End Of Class



public class MyCallable implements Callable<List<String>>{

@Override
public List<String> call() throws Exception {
List<String> ret = new Vector<String>();
for(int i=0; i<10; i++){
String temp = "안녕하세요 "+Thread.currentThread()+" 번 입니다.";
Thread.sleep(300);
ret.add(temp);
}
return ret;
}
}


Semaphore

import java.util.Random;
import java.util.concurrent.Semaphore;

public class SemaphoreTest {

      private static final Random rd = new Random(10000);

      static class Log {
             public static void debug(String strMessage) {
             System.out.println(Thread.currentThread().getName()  + " : " + strMessage);
            }
        }

      class SemaphoreResource extends Semaphore {

            private static final long serialVersionUID = 1L;

            public SemaphoreResource(final int permits) {
                   super(permits);
            }

           public void use() throws InterruptedException {

                  acquire(); // 세마포어 리소스 확보

                 try {
                        doUse();
                 } finally {
                       release(); // 세마포어 리소스 해제
                       Log.debug("Thread 종료 후 남은  permits: " +   this.availablePermits());
                }
             }

           protected void doUse() throws InterruptedException {

                // 임의의 프로그램을 실행하는데 거리는 가상의 시간
               int sleepTime = rd.nextInt(500);
               Thread.sleep(sleepTime); // 런타임 시간 설정
               Log.debug(" Thread 실행..................." + sleepTime);

                              /** something logic **/

              }

        }

     class MyThread extends Thread {

            private final SemaphoreResource resource;

            public MyThread(String threadName, SemaphoreResource resource) {
                   this.resource = resource;
                   this.setName(threadName);
            }

            @Override
             public void run() {
                  try {
                      resource.use();
                  } catch (InterruptedException e) {
                  } finally { }
             }

        }

     public static void main(String... s) {

          System.out.println("Test Start...");
          SemaphoreResource resource =  new SemaphoreTest().new SemaphoreResource(4);

         for (int i = 0; i < 20; i++) {
                new SemaphoreTest().new MyThread("Thread-" + i, resource) .start();
          }

      }

}




(http://tutorials.jenkov.com/java-concurrency/starvation-and-fairness.html 요약 )

자바 Starvation 요인 

  1. 은 우선순위의 쓰레드가 모든 CPU Time 을 소모한다.
  2. 쓰레드들은 synchronzed 블럭안에 들어가기위해 무한정 기다리며 블럭된다.
  3. 무작정 기다리며  wait() 가 불리기를 기다리는 쓰레드.  

포인트는 위의 요인들이 발생하는 이유는  무작위로 깨어난다는 점이다.


자바 쓰레드 점유율을  공정하게 만들기

다음 코드 블럭을 보자 

public class Synchronizer{

  public synchronized void doSynchronized(){
    //do a lot of work which takes a long time
  }

}

만약 doSynchronized 메소드에 쓰레드들이 경쟁할때 , 하나만 들어갈수있고, 나머지는 들어간놈이 나

올때까지 대기다. 근데 여러놈이 기다리는데 순서대로 나오질 못한다. 따라서 주구장창 대기하는놈이 

생길수도있다.


Synchronized 블럭대신 Locks 사용

public class Synchronizer{
  Lock lock = new Lock();

  public void doSynchronized() throws InterruptedException{
    this.lock.lock();
      //critical section, do a lot of work which takes a long time
    this.lock.unlock();
  }

}

synchronized 는 더이상 사용되지 않고 대신해 크리티컬 섹션이 사용된다.

public class Lock{
  private boolean isLocked      = false;
  private Thread  lockingThread = null;

  public synchronized void lock() throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked      = true;
    lockingThread = Thread.currentThread();
  }

  public synchronized void unlock(){
    if(this.lockingThread != Thread.currentThread()){
      throw new IllegalMonitorStateException(
        "Calling thread has not locked this lock");
    }
    isLocked      = false;
    lockingThread = null;
    notify();
  }
}

주저리 주저리 했는데, 결국 이 버전도 그닥 메리트가 없어보인다. 아래것이 핵심.


페어 락(Fair Lock)


public class FairLock {
    private boolean           isLocked       = false;
    private Thread            lockingThread  = null;
    private List<QueueObject> waitingThreads =
            new ArrayList<QueueObject>();

  public void lock() throws InterruptedException{
    QueueObject queueObject           = new QueueObject();
    boolean     isLockedForThisThread = true;
    synchronized(this){
        waitingThreads.add(queueObject);
    }

    while(isLockedForThisThread){
      synchronized(this){
        isLockedForThisThread =
            isLocked || waitingThreads.get(0) != queueObject;
        if(!isLockedForThisThread){
          isLocked = true;
           waitingThreads.remove(queueObject);
           lockingThread = Thread.currentThread();
           return;
         }
      }
      try{
        queueObject.doWait();
      }catch(InterruptedException e){
        synchronized(this) { waitingThreads.remove(queueObject); }
        throw e;
      }
    }
  }

  public synchronized void unlock(){
    if(this.lockingThread != Thread.currentThread()){
      throw new IllegalMonitorStateException(
        "Calling thread has not locked this lock");
    }
    isLocked      = false;
    lockingThread = null;
    if(waitingThreads.size() > 0){
      waitingThreads.get(0).doNotify();
    }
  }
}
public class QueueObject {

  private boolean isNotified = false;

  public synchronized void doWait() throws InterruptedException {
    while(!isNotified){
        this.wait();
    }
    this.isNotified = false;
  }

  public synchronized void doNotify() {
    this.isNotified = true;
    this.notify();
  }

  public boolean equals(Object o) {
    return this == o;
  }
}


포인트는 QueueObject 객체를  세마포어처럼 사용한다는 점이다.  각각의 대기하는 쓰레드가 

하나의 wait() 에 걸리는것이아니라, 각각의 wait()에 걸리게하고, 그것들을 큐로 관리하며, 

가장 먼저 큐에 들어간 쓰레드에 대해 notify 를  해주게되면 , 공평하게 쓰레드들이 점유율을 갖게된다

는 점. 


성능 문제

일반 lock 이나 synchronized 에 비해 느릴수 밖에 없는데, 자주 호출되거나, 크리티컬섹션사이에

작동되는 로직이 초간단하면 더더욱 성능에는 불리하다.





 1 번쓰레드가  synchronized  안으로 들어가면  2 번쓰레드는 synchronized 안으로 진입 불가.

 1 번쓰레드가 함수자체를 빠져나오면, 그때서야 2번 쓰레드는 함수 진입가능 

1 번쓰레드가  synchronized  안으로 들어가면  2 번쓰레드는 synchronized 안으로 진입 불가.

1 번쓰레드가 synchronized 를 빠져나오면, 그때서야 2번 쓰레드는 함수 진입가능 

1 번쓰레드가  synchronized  안으로 들어가면  2 번쓰레드는 synchronized 안으로 진입 불가.

1 번쓰레드가 5초후에 synchronized 를 빠져나오면, 그때서야 2번 쓰레드는 함수 진입가능 

          1 번쓰레드가 synchronized  안으로 들어가면 , 2번 쓰레드는 synchronized 에 대기하고있다가 

          1 번 쓰레드가 wait 에 진입하는 순간 , 2 번쓰레드는 synchronized 안으로 진입

          2 번 쓰레드도 wait 에 진입하고 ,

          1 번 쓰레드가 wait 에서 3초 대기후 빠져나오면서 synchronized 의 락을 다시 되찾고, 

          synchronized 의 락을 되돌려 주면서 종료

          2 번 쓰레드도 wait 에서 3초 대기후 빠져나옴. 



 1 번쓰레드가  1번째  synchronized  안으로 들어가고, 2번째 synchronized 안의 wait 에 진입해도

  2 번쓰레드는 synchronized 안으로 진입 불가.

  1 번쓰레드가 3초후 함수자체를 빠져나오면, 그때서야 2번 쓰레드는 함수 진입가능 



 1 번쓰레드가 synchronized  안으로 들어가면 , 2번 쓰레드는 synchronized 에 대기하고있다가 

 1 번 쓰레드가 wait 에 진입하는 순간 , 2 번쓰레드는 synchronized 안으로 진입

 2 번 쓰레드도 wait 에 진입하고 ,  두개의 쓰레드가 wait 하고 있을때 

 notify 가 호출되면 ??

 테스트에 의해서는 먼저 wait 된것이 먼저 빠져나왔는데, 문서에는 보장못한다고 한다. 

 주의할것은 notify() 하면 하나만 빠져나오지만, notifyAll() 하면 모두 빠져나온다.

 물론, 1번 , 2 번 쓰레드가 동시에 빠져나오는것은 아니며, 1번이 빠져나오면 synchronized 에 대한 

 락을 소유하기때문에, 2 번은 대기하다가, 1번이 락을 해제하면, 그때 2번이 락을 잡고 빠져나온다.

 

 따라서, 아래처럼  wait() 문 주위로 while 로 감싸주는 코드를 사용한다.(notifyAll 한번 호출에 하나씩 활동

 하기위해~notify 써도되긴 한다.근데 notify 를 쓰면 좀비쓰레드가 생길 가능성 up!!)


public synchronized void Func throws InterruptedException {       
while (!active) {
wait();
}
active = false;
}

하지만 위 처럼 하면, wait 에 2개의 쓰레드가 걸려있고, notifyAll() 가 2번 불려졌을때에도 
쓰레드중 하나만 빠져나올가능성이 있다.  (notifyAll 이 먼저 2번 호출되었을 경우) 

따라서 아래처럼 while 문의 조건을 list 로 둘수도있게된다.

public synchronized void Func() throws InterruptedException {
while (list.isEmpty()) {
wait(1000);
}
Integer i = list.pop();
}

public synchronized void Func2(int i ) {
notifyAll();
list.add(i);
}


나중에 또 잊어버리는것을 방지하고자 ;; 시간날때마다 하나씩 정리해둡니다.


케이스 1. 스프링 플젝에서 설정을 외부로 빼기위해, 자바 프로퍼티 파일을 사용



- 위의 this 위치에 파일을 생성하여 봅니다.  src/main/resources  폴더는 스프링 프로젝트 만들면
  자동으로 생기더라구요

- 해당 프로젝트를 war 로 만들면 WEB-INF/classes/  폴더아래에 WMOSConfig.properties 파일이
  위치해 있군요. 

- 클래스 로더를 사용해서 파일을 가져오도록 해봅니다.


테스트)

       String config ="resources/WMOSConfig.properties"; <-- 이거 안됩니다. 아래처럼 경로를

String config = "WMOSConfig.properties";

ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();

URL url = ClassLoader.getSystemResource(config);   <--- 안됨

URL url2 = currentThreadClassLoader.getResource(config);  <---- 잘됨 


         Thread.currentThread().getContextClassLoader();  <-- 이렇게 얻어온 클래스로더만 작동합니다. 

         * 웹 애플리케이션은 웹 컨테이너 안에서 실행되며, 각각의 웹 어플리케이션은 자신의 클래스 로더를 가짐.
          위의 WEB-INF/classes/ 경로는 웹어플리케이션의 클래스로더가 인지함.

         

솔루션)

- WMOSConfig.properties 파일안의 내용

trams-ip = 127.0.0.1


- 파일 내용 읽어오기 

Properties prop = new Properties();

   ClassLoader cl;

cl = Thread.currentThread().getContextClassLoader();

URL url = cl.getResource("WMOSConfig.properties");

prop.load(url.openStream());

   String ip = prop.getProperty("trams-ip");

   System.out.println(ip);  <--- 127.0.0.1






케이스 2.    log4j 의 파일 appender 에서 로그파일 위치는?

    <!-- 날짜별 로그  -->

    <appender name="file" class="org.apache.log4j.DailyRollingFileAppender">

        <param name="file" value="D:/testlogs/dailyout.log"/>

         .....

    </appender>


이렇게 하면 당연히, D:/testlogs/ 폴더에 날짜별로 생겨납니다.

   

  <!-- 날짜별 로그  -->

    <appender name="file" class="org.apache.log4j.DailyRollingFileAppender">

        <param name="file" value="./testlogs/mytestlog.log"/>

         .....

    </appender>


어떻게 될까요?


저기에서 ./  는 WAS 시작된 위치가 현재 디렉토리입니다.  따라서 톰캣을 사용한다면 

Tomcat 7.0/testlogs / 폴더에 날짜별로 생겨납니다. 



+ Recent posts