Java SE 8 의 새로운 기능.

2014/06/30 21:34

서비 JAVA

Java 8 의 closure, lambda, Nashorn 등 굵직한 변화에 묻혀 잘 화자되진 않지만 몇 가지 유용한 기능을 살펴보면
  1. Stamped Lock
    멀티 쓰레드 코드는 오랜 기간 (지금도 여전히) 서버 개발자의 골칫거리 중에 하나이다. 오랜 시간 Java Core에는 공유된 자원에 대해 최소한의 잠금시간을 위한 여러 라이브러리들이 추가되어 왔다. 대표적인 ReadWriteLock는 읽기와 쓰기를 위한 lock을 분리하여 잠금시간을  최소화할 수 있도록 지원( 동기화 클래스 - ReadWriteLock 인터페이스의 ReentrantReadWriteLock ) 하고 있다. 이게 이론상으로는 나이스한 아이디어 이긴한데 실제로 이 ReadWriteLock은 너무 느리다는 문제가 있다.
    이 문제를 해결하기 위해 Java 8에서는 StampedLock 이라는 새로운 ReadWrite lock을 소개하고 있다. 좋은 소식은 이 StampedLock은 정말 빠르다는거고 나쁜소식은 코드를 작성하는게 좀 더 복잡하다는거다. 이 lock은 reentrant 하지 않으며, 이는 곧 deadlocking에서 자유로움을 의미한다.
    [code java]StampedLock lock = new StampedLock();
    //빠른 non-blocking lock 획득
    long stamp = lock.tryOptimisticRead();   
    // 원하는 동작 작성.
    work();
    if (lock.validate(stamp)) {
    //work() 작업 성공. 다른 writer thread 와 경합 없었음.
    } else { // 다른 쓰레드의 write 작업으로 경합이 있다면 read lock으로 재시도.
    //스탬프를 변경하는동안 다른 스레드는 쓰기 잠금을 획득해야함.    
    //기존의 무거운 read lock으로 변경.
    //전통적인 blocking lock 획득
    stamp = lock.readLock(); 
    try {
    //다시 한번 시도
    work();
    } finally {
    //lock 해제
    lock.unlock(stamp);
    }
    }
    [/code]
    StampedLock과 기존의 ReadWriteLock , synchronized 과의 성능 비교 아티클도 있으니 참고.

  2. Concurrent Adder
    가장 기본적인 동시성 패턴은 읽기 / 쓰기에 대한 숫자 카운터이다. 이걸 처리하는데는 많은 방법이 있지만 Java 8에서 제공하는것 만큼 효율적이거나 우아하진 못했다. 지금까지는 sun.misc.Unsafe class를 활용한 CPU의 CAS( Compare and Swap ) 인스트럭션에 직접 counter를 설정하는 Atomics를 이용하는것이었다. 이 방식의 문제는 경합에의해 CAS에 실패하는 경우 AtomicInteger를 증가하면서 CAS에 성공할때까지 루프 상태가 되는데 이 부분이 성능의 발목을 잡고 있었다.
    Java8의 Concurrent Adder 계열의 클래스는 read / write에 함께 사용하는 편리한 방법을 제공한다. 엄청 간단해서 LongAdder 객체를 만들고 add() 나 intValue() 같은 메소드를 호출하기만 하면된다. 

    기존 Atomics와 Adder의 차이점은 경합에의한 CAS 실패 시 Adder는 CPU를 spin하는 대신 증분값(delta)를 해당 쓰레드를 위해 할당된 internal cell object에 기록한다. 그런 다음 intValue() 결과에 다른 보류중인 cell에 기록해둔 delta를 더한다. 이것으로 CAS를 시도하거나 다른 쓰레드를 block하는 횟수를 줄일 수 있게된다.

  3. Parallel Sorting
    conccurent Adder의 고속화에 힘입어 Java 8에서는 소팅을 고속화 할 수 있는 간결한 벙법을 제공한다. 사용방법도 상당히 단순하다.[code java]
    //기존방식
    Array.sort(arrayToSort);
    //새로운 방식
    Array.parallelSort(arrayToSort);
    [/code]

  4. new Date API
    Java 8 에서는 완전히 새로운 date-time API를 제공한다. 사용하기 편하고 정확도가 높은 joda time API가 Java core library에 포함 되었다.지금까지의 Date 관련 API는 대부분 deprecated 된다. 새로운 API는 기능적이면서도 우아하지만 아쉽게도 기존 api를 사용하는 방대한 코드는 여전히 남아있고 빠른시간에 새로운 api로 교체되지는 못할것이다.

    기존 api (java.util.Date java.sql.Date 등)와 신규 api의 갭을 메우기위해 기존 Date 객체를 Instant로 변경하기 위한 toInstant() 메소드와 ㅑInstant를 Date 로 변경하기 위한 from(Instant instant) static 메소드를 추가하였다. 이는 특히 새로운 API보다는 기존 API로 작업하는것을 선호하는 개발자에게 효과적일 수 있다.

  5. OS Process Control
    JNI를 통한 OS 프로세스 호출이 필요악일 지라도 자바 코드 내에서 OS 프로세스를 실행한 경우 실행중인 프로세스를 제어하기 어려웠다. 이를 개선하기위해 Process class에 다음 세개의 메소드를 추가하였다.
    - destroyFprcibly() : 기존 destroy() 보다 더 높은 프로세스 종료율을 제공.
    - isAlive() : java code를 통해 실행한 프로세스가 살아있는지 확인.
    - waitFor() : 새로 오버로드한 waitFor() 메소드를 통해 프로세스 종료를 얼마나  기다릴지 정할수 있음.

    프로세스가 끝나지 않은 경우 끝내고 다음 코드로 진행.
    [code java] if (process.wait(MY_TIMEOUT, TimeUnit.MILLISECONDS)){
    //success!
    } else {
    process.destroyForcibly();
    } [/code]
    코드가 완료되기 전 어떤 프로세스도 남겨두지 않으려 할 경우, 느리지만 확실하게 OS에서 사라지게하기.
    [code java] for (Process p : processes) { if (p.isAlive()) { p.destroyForcibly(); } } [/code]

  6. Overflow 대응 숫자 연산
    숫자 연산에서의 overflow는 가끔 끔찍한 버그를 만든다. 특히 int 값을 증가하는 count 값을 이용하는 시스템에서 특히 그런데, 개발계나 스테이징에서는 발견하지 못하고 오랜기간 운영하는 production 모드에서 발생하면 그야말로 재앙이다.

    이런 상황에 도움이 되고자 Java 8에는 부호에 민감한 계산에서 overflow발생 시 ArithmeticException을 던지는 새로운 exact 메소드가 추가되었다.
    [code java]
    //overflow 시 ArithmeticException 발생
    int multi = Math.multiplyExact( bigA, bigB ); [/code]
    이 새로운 메소드의 한가지 단점이라면 overflow가 발생할 코드의 위치를 찾는것은 개발자의 몫이란거다.

  7. Secure Random Generator
    Java는 근 몇년 간 보안 허점과 분투하고 있다. JVM과 Framework에 걸친 광범위한 보안조치가 취해지고 있다.  낮은 수준의 난수로 암호화 키를 생성하거나, 민감한 정보를 해시하는 경우도 해킹에 취약한 시스템을 만든다.

    오랜기간동안 Random Number Generator algorithm은 개발자 영역으로 남겨져 있었고. 구현체가 특정 하드웨어 / OS / JVM에 의존하는 경우, 원하는 알고리즘을 사용하지 못할 수 있었다. 문제는 이런 경우 응용 프로그램은 공격의 더 취약한 Generator를 기본값으로하는 경향이 있었다.

    Java 8에는 JVM 보안 공급자/알고리듬을 선택하기를 원하는 개발자를 위해 SecureRandom.getInstanceStrong() 를 새로이 추가하였다. 이 메소드는 hardware / OS / JVM에 대한 완전한 통제권이 없는 상황에서 ( cloud , PaaS 등.. ) 개발 할 때 적절한 솔루션이 될 수 있다.

  8. Optional 참조
    NullPointer는 말하자면 문지방에 찧은 발가락 같다. 실제로 저명한 언어 디자이너 / 석학 중 일부는 null reference를 고안한 건 엄청남 실수라고 말하는 사람도 있다. 나는 개인적으로 null reference 가 있는게 더 자연스럽다고 생각하는 쪽이긴 하지만... 여하튼 null pointer 관련 에러는 성가신 존재임에는 틀림없다. Java 8 에서는 이런 Null pointer 처리를 도와줄 Optional<T> 템플릿이 도입되었다.

    Scala와 Haskell에서 빌려온 이 템플릿은, 템플릿에 전달하거나 함수에의해 반환 된 참조가 null이 될 수있는 경우를 명시적으로 언급하는 것이 가능해졌다. 이렇게 함으로써 코드 작성 시 document 도움 없이도 런타임에 특정 참조가 null인 경우가 있을지 없을지 guess game 하는 정력 낭비를 줄여준다.
    [code java]Optional<User> tryFindUser(int userID) { }
    //혹은
    void processUser(User user, Optional<Cart> shoppingCart) { }
    [/code]또한, Optional 템플릿은 null 이외의 값을 사용할 수 있는지 확인하기 위해, isPresent() 람다에서는 ifPresent() 메소드를 제공한다.
2014/06/30 21:34 2014/06/30 21:34
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

lambda 와 closure 의 차이점 ?

Java SE 8에 도입되기 전부터 관심을 받던 람다식과 closure. 이 둘의 차이는 뭘까?

한마디로 lambda는 이름이 없는 익명 함수( anonymous function ) 를 가르키며,
closure는 하나 이상의 엮인(bound) 변수가 있는 환경에서 실행( or 평가)되는 함수 정도로 이해할 수 있겠다.

자유변수(메소드 파라메터도 아니고 코드 내부에도 저장되지 않는 변수)의 값을 포함하는 코드 블록의 기술용어가 클로저 closure이다.누군가 자신이 사용하는 언어에 클로저가 있다고 흐뭇해한다면, 자바에도 클로저가 있으니 안심해도 좋다. 자바에서는 람다 표현식이 클로저다. 사실 이너 클래스가 클로저로 존재해왔다. 다만, 자바8에서는 매력적인 문법으로 클로저를 제공한다.
- 가장 빨리 만나는 자바8 (길벗) 에서.


두 의미에 대한 깊은 논의는 다음 링크에서 확인 할 수 있음.
http://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda
http://www.langdev.org/posts/38

그럼, Clojure 는?
http://clojure.org/ 에서 정의하고 있는것과 같이 JVM(Java Virtual Macine) , CLR(Common Language Runtime), JavaScript 엔진에서의 동작을 목표하는 dynamic programing language. 단어를 처음 접했을 때 정의를 찾아보지 않고, Kroisse님이 알려주기전까지 두 단어가 혼용되어 쓰이고 있다고 적당해 생각해 버렸던게 부끄럽넹...

2014/06/25 15:13 2014/06/25 15:13
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. Clojure는 프로그래밍 언어 이름이고, 말씀하시는 개념은 closure입니다.

  2. Blog Icon
    서비

    Kroisse님. 고맙습니다.
    영미권 검색 시에 Clojure, closure 모두 검색되어서 혼용해서 사용하는 줄 알았습니다. 알려주신 내용으로 좀 더 명확하게 공부하도록 하겠습니다.

java se 8 에서 람다식 사용하기 - 기초

java 8 에 람다 표현식이 도입되면서 closure를 사용하지 않고도 함수형 언어 비슷한 코드를 작성하는게 가능해졌다. 람다 표현식은 이름이 없는 익명 함수를 의미한다.
람다 표현식에 관심을 둬야하는 몇가지 이유는 다음과 같다.
  • OOP 언어인 Java에서 함수형 코드를 작성할 수 있게 해준다.
  • 람다 표현식을 이용함으로써 간결하고 명확한 코드로 그 의도를 표현할 수 있다.
  • Collection filtering, Iteration , Extraction 등에서 놀라운 코드 생산성을 보인다.


아주 아주 기본적인 람다 표현식에 대해 익혀보자.
Syntax
[code javascript](argument) -> (body)[/code]
0개 이상의 인자를 실행하고자 하는 함수 body 로 전달한다는 의미이며, 이는 람다 표현식의 일반적인 구문이다.
예를 들면 다음과 같이 표현된다.
[code javascript](int a, int b) -> a*b
(Person p) -> { return p.getAge();}[/code]

다음 코드는 swing 에서 자주 보이는 이벤트 핸들러를 람다로 변경하는 코드이다.
[code java]btnOk.setOnAction( newEventHandler() {
 @Override
 public voidhandle(ActionEvent e) { //버튼 action에 처리할 코드를 여기에 작성
 }
});[/code]
위 코드를 람다 표현식으로 변경하면 다음과 같다.
[code javascript]btnOk.setOnAction( e -> { //버튼 action에 처리할 코드를 여기에 작성
});[/code]

표현식의 또 다른 예로 익명 클래스는 다음과 같이 작성할 수 있다.
[code java]new Thread(
() -> System.out.println("This is a seperate thread")
).start();[/code]

마지막으로 collection을 다루는 예제만 더 보고 끝내자.
[code java]ArrayList arrayList= new ArrayList<>();
for(int i=1; i<=100; i++)
arrayList.add(i);

//일반적인 for-loop 문
for(Integer i: arrayList)
{ System.out.println(i);
}

//lambda식으로 표현한 for-loop문
arrayList.forEach( i -> {System.out.println(i);});[/code]

이상으로 람다 표현식이 어떻게 코드 수를 줄이고 Java와 함수형 언어 사이의 갭을 매워주는지에 대해 간략히 알아보았다.

2014/06/25 14:32 2014/06/25 14:32
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

replaceAll(" ","") trim() 으로 제거되지 않는 공백 제거

[code java]String str =originalString.replaceAll(" ", "");[/code] 등으로 삭제되지 않는 공백문자를 제거 해 보자.

상기 코드로 삭제되지 않는 이유는.. cjk 문자셋에서 나타나는 IDEOGRAPHIC SPACE 라 불리는 유니코드 \u3000 , HTML 표현으로는 &#12288; 문자로 폰트 지원이 없으면 눈에 보이지 않는(display 되지 않는) 코드로만 존재하는 공백이기 때문.
http://www.fileformat.info/info/unicode/char/3000/index.htm

이럴 경우 다음과 같은 정규식을 통해 제거가 가능.

모든 공백 제거
[code java]String str =originalString.replaceAll("\\p{Z}", "");[/code]

앞뒤 공백만 제거(trim)
[code java]String str =originalString.replaceAll("(^\\p{Z}+|\\p{Z}+$)", "");[/code]
2014/06/17 18:45 2014/06/17 18:45
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. 좋은글 감사합니다.

    퍼가요~

  2. Blog Icon
    euna

    감사합니다. trim이 안되서 애먹고 있었는데 해결했네요. 정말 감사합니다

근래 JAVA 쪽에는 어떤 일들이 있나?

문득 문득 현재 JAVA의 위상이나 JAVA 다음은 무엇일까 등을 생각해 보는데요...  그에 대한 힌트를 얻을 수 있는 발표 자료가 있어 공유합니다. Zeroturnaround 社에서 2,164 명의 개발자 설문 결과를 정리/배포한 자료입니다.
현재 java 진영의 트렌드에 초점이 맞춰져있고 국내 상황과도 다소 차이가 있을 수 있지만 개발 현장의 광범위하고 객관적인 자료 정도로 생각하고 훑어보시면 되겠습니다.

레포트 원문은 http://zeroturnaround.com/rebellabs/java-tools-and-technologies-landscape-for-2014/ 입니다.

아래 쪽에 첨부한 pdf 내용 중 저에게는 다음 항목들이 눈길을 끌었습니다.

  • 16페이지의 The next JVM language to learn : 역시 scala의 기세는 대단하군.. Clojure가 Groovy를 제칠 수 있을까..? 그렇다면 그 시점은?
  • 18페이지의 IDE used most open : 아.. 제가 애용하는 넷빈즈의 참담한 점유율.. 상용인 Intellij IDEA 보다 낮다니.. 너 이자식 힘내라!
  • 21페이지의 Build tool used most open : Maven이 대세인건 부정할 수 없는 현실. Ant야 기존 레거시 시스템 덕으로 2위인거고... Gradle이 현재의 메이븐의 위상을 갖추는건 언제쯤일까..
  • 31페이지의 ORM framework in use : MyBatis 사용율이 의외로 낮네.. 국내는 전자정부 표준 프레임워크 영향으로 사용율이 높은건가...
  • 43페이지의 VCS technologes used : Git은 이미 대세구나... 국내 기업체도 2-3년 내에 SVN에서 Git으로 넘어가지 않을까...


레포트의 말미에는 2015년도 예상 우선 순위도 게시되어 있습니다.

java-tools-and-technologies-2014.pdf

JAVA TOOLS AND TECHNOLOGIES LANDSCAPE FOR 2014






2014/06/09 16:04 2014/06/09 16:04
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

2014년 java 진영 웹 프레임워크

2014년 기준으로 java 진영에서 웹개발에 사용하고 있는 프레임워크 동향을 JRebel 제품으로 유명한 Zeroturnaround 社에서 레포팅했습니다. 역시나 SpringMVC가 가장 널리 활용되고 있으며 JSF , Vaadin 이 그 뒤를 잇고 있습니다. 발표 자료가 자료니 만큼 국내와는 사정이 다르게 JSF 사용율이 굉장히 높네요..

2014년 웹프레임워크 동향

출처 : https://twitter.com/RebelLabs/status/469176327007117312/photo/1





2014/06/09 15:41 2014/06/09 15:41
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Spring4 - Servlet3.0 Websocket ConcurrentModificationException 익셉션 발생 시

Spring 4 에서 지원하는 @MessageMapping 어노테이션으로 구현한 WebSocket 서버 프로그램에서 Exception in thread "clientInboundChannel-5" java.util.ConcurrentModificationException 와 같은 익셉션 발생 시.
스프링 프레임워크 버전을 확인.  4.0.5 에서 해결된 문제로 이하 버전으로 개발했다면 프레임워크 업데이트로 해결 가능.
https://jira.spring.io/browse/SPR-11755
[code] Exception in thread "clientInboundChannel-5" java.util.ConcurrentModificationException at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:711) at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:734) at org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry$DestinationCache.updateAfterNewSubscription(DefaultSubscriptionRegistry.java:179) at org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry.addSubscriptionInternal(DefaultSubscriptionRegistry.java:91) at org.springframework.messaging.simp.broker.AbstractSubscriptionRegistry.registerSubscription(AbstractSubscriptionRegistry.java:65) at org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler.handleMessageInternal(SimpleBrokerMessageHandler.java:126) at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.handleMessage(AbstractBrokerMessageHandler.java:171) at org.springframework.messaging.support.ExecutorSubscribableChannel$1.run(ExecutorSubscribableChannel.java:70) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Exception in thread "clientInboundChannel-4" java.util.ConcurrentModificationException at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:711) at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:734) at org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry$DestinationCache.updateAfterRemovedSession(DefaultSubscriptionRegistry.java:214) at org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry.unregisterAllSubscriptions(DefaultSubscriptionRegistry.java:112) at org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler.handleMessageInternal(SimpleBrokerMessageHandler.java:136) at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.handleMessage(AbstractBrokerMessageHandler.java:171) at org.springframework.messaging.support.ExecutorSubscribableChannel$1.run(ExecutorSubscribableChannel.java:70) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) [/code]
2014/06/03 15:59 2014/06/03 15:59
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다