Programming/Java

스트림, IO Stream, 직렬화(Serializable), 역직렬화(Deserializable), 스레드

김심슨 2025. 6. 11. 10:49

스트림 https://ryuzyproject.tistory.com/169

 

스트림

1. 스트림스트림(Stream)은 자바 8에서 도입된 기능으로, 컬렉션이나 배열의 데이터를 반복문 없이 함수형 스타일로 처리할 수 있도록 도와주는 도구입니다. 스트림은 데이터 저장소가 아니라, 데

ryuzyproject.tistory.com

 

 

package lesson08;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Ex01_Main {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "orange", "apple");
        System.out.println(fruits);
        /*
            String :: toUpperCase
            - 메서드 참조
            - s -> s.toUpperCase()와 같은 람다식을 더 간단하게 표현
            - String 클래스의 toUpperCase() 인스턴스 메서드를 현재 스트림의 각 요소에 대해 호출하겠다는 뜻
            
        */
        fruits.stream()
                // a로 시작하는 걸 찾아내서 stream 객체로 반환
                .filter(f -> f.startsWith("a"))
                // 대문자로 바꿔서 반환하세요 (중간연산, 아직 안꺼냄)
                .map(String::toUpperCase)
                // 중복 제거
                .distinct()
                // 반복하면서 찍어줘!
                .forEach(System.out::println); // APPLE
    }
}

 

package lesson08;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Ex03_Main {
    public static void main(String[] args) {
        try (
                FileReader reader = new FileReader("src/input.txt");
                FileWriter writer = new FileWriter("output2.txt");
                ) {
            int ch;
            while((ch=reader.read())!= -1) {
                writer.write(ch);
            }
            System.out.println("파일 복사 완료(문자 스트림)");
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

 

https://ryuzyproject.tistory.com/170?category=1210552

 

스레드

1. 스레드스레드(Thread)는 하나의 프로세스 내에서 독립적으로 실행되는 실행 흐름 단위를 말합니다. 일반적으로 하나의 프로그램(프로세스)은 하나 이상의 스레드를 가질 수 있으며, 이를 통해

ryuzyproject.tistory.com

 

 

 

 

 

종합 예제 : 점원별 햄버거 만들기 

package lesson08;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

class Ex14_Order {
    private final int orderId;
    public Ex14_Order(int orderId) {
        this.orderId = orderId; //id 번호를 받아다가 저장해 줄 생성자
    }
    public int getOrderId() {
        return orderId;
    }
}

//Callable을 class로 구현해서 사용하기 (Runnable을 이용하면 return하지 못하기 때문)
class OrderProcessor implements Callable<String> {
    private final Ex14_Order order;
    private final String workerName;
    private static final Random random = new Random();

    private  static final Map<String, int[] > workerSpeedMap = Map.of(
            "김사과",new int[] {1000,2000}, //숫자가 작을수록 빨라용~
            "반하나",new int[] {2000,3000},
            "오렌지",new int[] {3000,4000}
    );
    private static final AtomicInteger totalProcessed = new AtomicInteger(0); //고전적인 방법으로 멀티스레스방법에서 값을 수정할 때 동시다발적으로 일어날 수 있으니까 싱크로나이즈를 썼었음. 근데 AtomicInteger은 멀티 스레드일 때 값을 알아서 충돌나지 않게 해준다. 값을 더하거나 빼거나 연산을 할 때 이 객체에 있는 값은 멀티스레드 환경에서 충돌나지 않음
    private static final AtomicInteger processed = new AtomicInteger(0);
    private static final Map<String, Integer> workerStats = new ConcurrentHashMap<>();
    //맵을 쓰는데 스레드에 안정된 클래스

    public OrderProcessor(Ex14_Order order, String workerName) {
        this.order = order;
        this.workerName = workerName;
    }

    @Override
    public String call() throws Exception {
        int[] speedRange = workerSpeedMap.get(workerName);
        int prepTime = random.nextInt(speedRange[1] -  speedRange[0] + 1) + speedRange[0];
        Thread.sleep(prepTime);
        // 주문 1건이 처리되었으니, 전체 주문 수를 1 증가시켜라 
        totalProcessed.incrementAndGet();
        // workerName : 아르바이트생 이름 
        // 1 : 처리한 주문 수
        // 알바생 이름이 Map에 없으면 (workerName, 1)로 추가 
        // 이미 있다면 ("김사과", 기존값+1)로 업데이트 해줘! (Integer::sum)
        workerStats.merge(workerName,1, Integer::sum );
        return workerName + " - 주문 " + order.getOrderId() + " 번 완료 (소요시간 : " + prepTime + "ms)";
    }
    
    public static int getTotalProcessed() {
        return totalProcessed.get();
    }
    public static void printStats() {
        System.out.println("점원별 처리 주문 수");
        workerStats.forEach((name,count)-> System.out.println(name + " : " + count + "건"));
    }
}

public class Ex14_Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        int totalOrders = 10; //전체 주문이 10개
        List<String> workers = List.of("김사과", "반하나", "오렌지");
        ExecutorService executor = Executors.newFixedThreadPool(workers.size());
        List<Future<String>> futures = new ArrayList<>(); //리턴 받을 Future을 만든다! 배열에 저장

        for (int i = 0; i < totalOrders; i++) {
            String worker = workers.get(i % workers.size()); //리턴된 걸 가져오는데, 사이즈만큼 나눌 것.
            Ex14_Order order = new Ex14_Order(i);
            futures.add(executor.submit(new OrderProcessor(order, worker)));
            // futures에 결과값들이 쌓일 것
        }
        for (Future<String> future : futures) {
            System.out.println(future.get());
        }
        
        executor.shutdown();
        OrderProcessor.printStats();
        System.out.println("총 주문 처리 수 : " + OrderProcessor.getTotalProcessed() + "건");

    }
}

-------------------------------------------------------

"C:\Program Files\Java\jdk-17\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2025.1.1.1\lib\idea_rt.jar=51877" -Dfile.encoding=UTF-8 -classpath C:\ai_lsb\Java\programming\out\production\programming lesson08.Ex14_Main
김사과 - 주문 0 번 완료 (소요시간 : 1382ms)
반하나 - 주문 1 번 완료 (소요시간 : 2793ms)
오렌지 - 주문 2 번 완료 (소요시간 : 3113ms)
김사과 - 주문 3 번 완료 (소요시간 : 1011ms)
반하나 - 주문 4 번 완료 (소요시간 : 2572ms)
오렌지 - 주문 5 번 완료 (소요시간 : 3314ms)
김사과 - 주문 6 번 완료 (소요시간 : 1347ms)
반하나 - 주문 7 번 완료 (소요시간 : 2059ms)
오렌지 - 주문 8 번 완료 (소요시간 : 3717ms)
김사과 - 주문 9 번 완료 (소요시간 : 1019ms)
점원별 처리 주문 수
반하나 : 3건
김사과 : 4건
오렌지 : 3건
총 주문 처리 수 : 10건

종료 코드 0(으)로 완료된 프로세스