donaricano-btn

Java8 - 함수형인터페이스 그리고 람다의 활용


1. 실행 어라운드 패턴에 적용

- 데이터베이스의 파일 처리의 경우 자원 오픈, 처리, 자원 클로즈의 절차를 갖는다. 

- 실제 자원을 처리하는 코드가 설정과 정리과정 사이에 존재 한다면 이를 실행 어라운드 패턴이라 부른다.

1
2
3
4
5
6
    //파일의 한줄 읽는다
public static String processFile() throws IOException{
    try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))){
        return br.readLine();
    }
}


2. 함수형 인터페이스를 이용해서 동작 전달

1) 함수형 인터페이스란?

- 정확히 하나의 추상 메서드를 지정하는 인터페이스이다.(Comparator, Runnable)

1
2
3
4
@FunctionalInterface
public interface Predicate<T>{
  boolean test(T t)
}

2) BufferedReaderProcessor.class

 

1
2
3
4
@FunctionalInterface
public interface BufferedReaderProcessor{
  String process(BufferedReader b) throws IOException;
}

3) 실행어라운드 패턴 수정

 

1
2
3
4
5
6
    //동작파라미터화로 변
public static String processFile(BufferedReaderProcessor p) throws IOException{
    try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))){
        return p.process(br);
    }
}


3. 람다 전달

 

1
2
String oneLine = processFile((BufferedReader br) -> br.readLine());
String twoLine = processFile((BufferedReader br) -> br.readLine() + br.readLine());


블로그 이미지

리딩리드

,
donaricano-btn

Java8 - 동작파라미터화 (predicate 활용)


1. 문제

- 사과 리스트 중에 150 그램 이상의 빨간 사과를 찾아라


2. 기존 자바의 필터링

1
2
3
4
5
6
7
8
9
   public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p){
    List<Apple> result = new ArrayList<>();
    for(Apple apple : inventory) {
        if("red".equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

- 색상이 빨간색 사과만 골라내서 담는다.

- 그러나 추가적으로 요구사항이 생길 경우 일일이 변경해야 한다.


3.  디자인 패턴을 이용한 필터링

1) filterApple 메소드

1
2
3
4
5
6
7
8
9
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p){
    List<Apple> result = new ArrayList<>();
    for(Apple apple : inventory) {
        if(p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

2) interface - predicate 사용

1
2
3
public interface ApplePredicate {
    boolean test(Apple apple);
}

3) implements 

1
2
3
4
5
public class AppleRedAndHeavyPredicate implements ApplePredicate{
    public boolean test(Apple apple) {
        return "red".equals(apple.getColor()) && apple.getWeight() > 150;
    }
}

- 기존 자바의 필터링보다 유연하게 대처 가능하다. 그러나 interface 를 따로 만들어야 한다는 번거로움이 있다.


4. 익명클래스 사용한 필터링

1) filterApple

1
2
3
4
5
       List<Apple> redAndHeavyApples = filterApples(inventory, new ApplePredicate() {
    public boolean test(Apple apple) {
        return "red".equals(apple.getColor());
    }
});

- 익명클래스 사용하면 간결하다. 그러나 가독성이 떨어져 한눈에 보기 힘들다.


5. 람다 표현식 사용

1
List<Apple> redAndHeavyApples = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));

- 간결해졌다.


6. 리스트의 추상화와 람다를 이용한 필터링

1) interface

1
2
3
public interface Predicate<T> {
    boolean test(T t);
}

2) filter() 

1
2
3
4
5
6
7
8
9
10
   public static <T> List<T> filter(List<T> list, Predicate<T> p){
     
    List<T> result = new ArrayList<>();
    for(T e : list) {
        if(p.test(e)) {
            result.add(e);
        }
    }
    return result;
}

3) 사용

1
2
3
List<Apple> redAndHeavyApples = filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));
         
List<String> evenNumber = filter(numbers, (Integer i) -> i%2 ==0);

- 추상화를 통하여 간결함을 물론 다양한 형태를 필터링 할 수 있다.





블로그 이미지

리딩리드

,
donaricano-btn

Future, Callable, Executor를 이용한 Thread


1. ExecutorService

-  자바5에서 멀티스레드와 콜백을 간편하게 사용하기 위하여 ExecutorService를 사용한다.

1) 사용법

- 작업 대상의 Callable 객체를 만들고 ExecutorService에 등록한 다음 Future 객체를 반환 받는다.

- 작업 결과는 Future를 통해 얻는다.


2. 예제

1) FindMax.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.concurrent.Callable;
 
public class FindMax implements Callable<Integer>{
     
    private int[] data;
    private int start;
    private int end;
     
    FindMax(int[] data, int start, int end){
        this.data = data;
        this.start = start;
        this.end = end;
    }
     
    public Integer call() {
        int max = 0;
        for(int i = start; i<end; i++) {
            if(data[i] > max) {
                max = data[i];
            }
        }
         
        return max;
    }
}

- 값을 비교하여 max값을 반환 한다.

- Callable 인터페이스는 어떤 값도 반환하는 call() 를 제공한다.


2) MultiThreadMaxFinder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 
public class MultiThreadMaxFinder {
     
    public static int max(int[] data) throws InterruptedException, ExecutionException {
         
        if(data.length == 1) {
            return data[0];
        }else if(data.length == 0) {
            throw new IllegalArgumentException();
        }
         
        //작업 분할
        FindMax task1 = new FindMax(data, 0, data.length/2);
        FindMax task2 = new FindMax(data, data.length/2, data.length);
         
        //두 개의 스레드 생
        ExecutorService service = Executors.newFixedThreadPool(2);
         
        Future<Integer> future1 = service.submit(task1);
        Future<Integer> future2 = service.submit(task2);
         
        return Math.max(future1.get(), future2.get());
         
    }
     
    public static void main(String[] args) throws InterruptedException, ExecutionException {
         
        int[] values = {1,4,5,7,10,2,77,100,55,9,11,23};
         
        System.out.println(MultiThreadMaxFinder.max(values));
    }
}

- Callable 객체를 Executor 에 추가하여 사용한다.

- Executor는 각 객체의 스레드 실행


3. 장점

- 멀티 코어 환경에서 두배의 가까운 성능을 보인다. 

- 비동기 문제를 해결 할 수 있다.


블로그 이미지

리딩리드

,
donaricano-btn

Thread에서 데이터 반환(폴링, 콜백) - Receive data by using thread polling, callback


1. thread의 데이터 반환

- run(), start() 는 어떤 값도 반환하지 않는다. 

- 특정한 방법이 필요하다


2. 일반 적인 데이터 반환

1) ThreadTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class ThreadTest implements Runnable{
     
    private String fileName;
    private String result;
     
    public ThreadTest(String fileName) {
        this.fileName = fileName;
    }
     
    @Override
    public void run() {
         
        FileOutputStream out = null;
        String filePath = "/Users/jeonghui/eclipse-workspace/JavaTest/src/";
        String content = "Hello";
         
        try {
            out = new FileOutputStream(filePath + fileName);
            out.write(content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
                //결과 셋
                result = fileName;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
         
    }
     
    public String getResult() {
        return result;
    }
     
}

- getResult() 를 이용하여 파일 쓰기가 끝나면 파일 이름을 반환한다.

2) ThreadInterface.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.IOException;
 
public class ThreadInterface {
     
    public static void main(String[] args) throws IOException {
         
        String[] files = {"abc0.txt","abc1.txt","abc2.txt"};
         
        for(String filename : files) {
            ThreadTest tt = new ThreadTest(filename);
            Thread t = new Thread(tt);
            t.start();
             
            //결과 출력
            System.out.println(tt.getResult());
        }
         
    }
}

- getResult() 를 호출하여 확인한다.

3) 문제

- 그러나 콘솔에는 null 값이 출력되거나 운 좋다면 파일 명이 나올 수 있다.

- 스레드의 처리 속도와 main() 의 속도가 불일치(경쟁조건) 하다.


3.  폴링(polling)을 이용한 경쟁조건 해결

1) 폴링(polling) 이란?

-  동기화를 목적으로 상태를 주기적으로 검사하여 일정 조건이 만족하면 처리하는 방법

2) 위의 문제 해결

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.io.IOException;
 
public class ThreadInterface {
     
    public static void main(String[] args) throws IOException {
         
        String[] files = {"abc0.txt","abc1.txt","abc2.txt"};
        ThreadTest[] tt = new ThreadTest[files.length];
         
        for(int i = 0; i<files.length; i++) {
            tt[i] = new ThreadTest(files[i]);
            Thread t = new Thread(tt[i]);
            t.start();
        }
         
        for(int i = 0; i<files.length; i++) {
            //결과 출력
            while(true) {
                if(tt[i].getResult() != null) {
                    System.out.println(tt[i].getResult());
                    break;
                }
            }
             
        }
         
    }
}

- 반복적으로 null 을 체크하여 아니라면 출력한다.

3) 문제

- 체크하기 위하여 너무 많은 일을 한다.

- 메인 스레드가 작업종료 상태를 계속 확인해야 하기 때문에 다른 스레드가 일할 시간 없다(성능저하)


4. 콜백(callback) 으로 문제 해결

1) 콜백(callback) 이란?

- 단일 스레드의 작업이 끝났을 때 자신을 생성한 클래스를 호출하여  메인 스레드에게 결과를 알려준다.

- 폴링의 무한 루프로 결과값을 체크하는 문제 해결

1) ThreadTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class ThreadTest implements Runnable{
     
    private String fileName;
    private String result;
     
    public ThreadTest(String fileName) {
        this.fileName = fileName;
    }
     
    @Override
    public void run() {
         
        FileOutputStream out = null;
        String filePath = "/Users/jeonghui/eclipse-workspace/JavaTest/src/";
        String content = "Hello";
         
        try {
            out = new FileOutputStream(filePath + fileName);
            out.write(content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
                //결과 셋
                ThreadInterface.ThreadResult(fileName);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
         
    }
     
    public String getResult() {
        return result;
    }
     
}

- 정적 메소드를 호출하여 결과값을 전달한다.

2) ThreadInterface.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.IOException;
 
public class ThreadInterface {
     
    public static void ThreadResult(String name) {
        System.out.println(name);
    }
     
    public static void main(String[] args) throws IOException {
         
        String[] files = {"abc0.txt","abc1.txt","abc2.txt"};
        ThreadTest[] tt = new ThreadTest[files.length];
         
        for(int i = 0; i<files.length; i++) {
            tt[i] = new ThreadTest(files[i]);
            Thread t = new Thread(tt[i]);
            t.start();
        }
    }
}

- 반환 값을 받기위한 정적메소드 

3) 개선

- 정적메소드를 호출하는 것보다 인스턴스를 생성하여 반환값을 받는 것이 더 좋다.

4) 문제

- 스레드의 순서가 없다. 먼저 종료되는 스레드의 결과를 먼저 받는다.

- 순서가 필요한 결과 값이라면 다른 방법이 필요하다.(Future, callable, Executor)




블로그 이미지

리딩리드

,