카테고리 없음

2024. 7. 19. (금) 슈퍼코딩 부트캠프 Day 41 / 주특기 4주차

태영9922 2024. 7. 19. 15:33

 

 

1. Java Mutli Threading 주의할 점

  • 순서가 보장되지 않는다 -> 여러 Thread가 공통 영역에 동시 접근하여 수정하는 상황 -> 충돌과 일관성 문제 발생
  • Thread 동기화 문제 해결 : 먼저 실행 중인 thread가 작업을 끝낸 후 다음 thread가 실행되도록 Synchronized로 해결
public class IncrementRunnable implements Runnable {
    private Counter counter;

    public IncrementRunnable(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter.increment();
            System.out.println("Count : " + counter.getCount());
        }
    }
}
-------------------
public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}
--------------------
public class JavaMultiThreadingIssue {
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread thread1 = new Thread(new IncrementRunnable(counter));
        Thread thread2 = new Thread(new IncrementRunnable(counter));
        Thread thread3 = new Thread(new IncrementRunnable(counter));

        thread1.start();
        thread2.start();
        thread3.start();
    }
}
 
  • Java 서버와 멀티 Threading
import java.io.*;
import java.net.Socket;
import java.util.List;

public class RequestHandler implements Runnable {
    private Socket clientSocket;
    private List<Customer> customerList;

    public RequestHandler(Socket clientSocket, List<Customer> customerList) {
        this.clientSocket = clientSocket;
        this.customerList = customerList;
    }

    @Override
    public void run() {
        System.out.println("Client connected");

        //TODO : 데이터를 받기 위한 InputStream 생성
        InputStream clientInputStream = null; //클라이언트로부터 받아온 스트림
        try {
            clientInputStream = clientSocket.getInputStream();
            ObjectInputStream ois = new ObjectInputStream(clientInputStream); //클라이언트에서 받아온 스트림을 Object로 읽기 위한 준비

            //TODO : 데이터를 보내기위한 OutputStream 생성
            OutputStream serverOutputStream = clientSocket.getOutputStream();
            PrintWriter printWriter = new PrintWriter(serverOutputStream, true);

            Customer customer = (Customer) ois.readObject(); //클라이언트에서 보낸 Customer 객체를 새로운 객체로 생성

            ListUtils.addList(customerList, customer);
            System.out.println("Thread " + Thread.currentThread().getName() + ": " + customer + "가 대기명단에 추가되었음");

            //TODO : 클라이언트에게 응답을 보냄
            printWriter.println("현재 고객대기명단은 " + customerList);

            clientSocket.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

    }
}

----------------------------
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ServerAdvance {
    public static void main(String[] args) {
        List<Customer> customerList = new ArrayList<>();

        try (ServerSocket serverSocket = new ServerSocket(1234))//TODO : 서버 소켓 생성
        {
            System.out.println("Thread " + Thread.currentThread().getName() + ": Server Started");

            while (true) {
                try {//TODO : 클라이언트 접속 대기
                    Socket clientSocket = serverSocket.accept();

                    Thread request = new Thread(new RequestHandler(clientSocket, customerList));
                    request.start();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

1. 디자인 패턴이란

  • 소프트웨어 디자인 과정 (= 코드 구현 전 설계) 전형적인 해결책
  • 디자인 패턴 = 게임 공략법, 전략과 비슷하다. 상황별 최적의 지식 및 노하우.
  • 상황에 맞춰서 유동적으로 적용이 필요하다.
  • 왜 배워야 하나? 실무에서 의사소통 시 많이 사용됨. Java 프레임워크, 라이브러리 내부에 구현되어 있음.

 

 

2. 디자인패턴 둘러보기

  • 생성 / 구조 / 행동 패턴으로 나누어짐
  • 생성패턴 : 기존 코드의 유연성과 재사용성을 증가시키는 객체 생성 매커니즘
  • 구조패턴 : 객체들과 클래스들의 구조를 유연하고 효율적으로 유지하면서 더 큰 구조로 조립하는 방법
  • 행동패턴 : 알고리즘 및 객체 간의 책임 할당과 관련

 

3. 대표 4가지 디자인 패턴

  • 싱글톤 패턴 : 단 하나의 인스턴스만 생성 및 공유하여 자원 절약 일관성 유지 목적

- 싱글톤 사용 전

public class FileWriterExampleTest {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            FileWriterExample writer = new FileWriterExample("src/exercise/chapter_60/singleTon/test.txt");
            writer.writeToFile("Thread 1: Message 1");
            writer.writeToFile("Thread 1: Message 2");
        });

        Thread thread2 = new Thread(() -> {
            FileWriterExample writer = new FileWriterExample("src/exercise/chapter_60/singleTon/test.txt");
            writer.writeToFile("Thread 2: Message 3");
            writer.writeToFile("Thread 2: Message 4");
        });

        Thread thread3 = new Thread(() -> {
            FileWriterExample writer = new FileWriterExample("src/exercise/chapter_60/singleTon/test.txt");
            writer.writeToFile("Thread 3: Message 5");
            writer.writeToFile("Thread 3: Message 6");
        });
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
 

- 싱글톤 사용 후

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

public class FileWriterSingleton {
    private static FileWriterSingleton instance = null;
    private FileWriter fileWriter;

    public FileWriterSingleton() {
        try {
            this.fileWriter = new FileWriter("src/exercise/chapter_60/singleTon/test.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public synchronized static FileWriterSingleton getInstance() {
        if (instance == null) {
            instance = new FileWriterSingleton();
        }
        return instance;
    }
}
---------------------------
public class FileWriterSingletonTest {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            FileWriterSingleton writer = FileWriterSingleton.getInstance();
            writer.writeToFile("Thread 1: Message 1");
            writer.writeToFile("Thread 1: Message 2");
        });

        Thread thread2 = new Thread(() -> {
            FileWriterSingleton writer = FileWriterSingleton.getInstance();
            writer.writeToFile("Thread 2: Message 3");
            writer.writeToFile("Thread 2: Message 4");
        });
        Thread thread3 = new Thread(() -> {
            FileWriterSingleton writer = FileWriterSingleton.getInstance();
            writer.writeToFile("Thread 3: Message 5");
            writer.writeToFile("Thread 3: Message 6");
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

 

  • 빌더 패턴 : 복잡한 객체의 생성 과정을 단순화, 가독성와 유연성을 높여 객체를 생성

- 빌더패턴 사용 전 객체 생성

public class User {
    private String firstName;
    private String lastName;
    private int age;
    private String email;

    public User(String firstName, String lastName, int age, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.email = email;
    }
}
---------------------------------
public class BuilderTest {
    public static void main(String[] args) {

        User user1 = new User("John", "Doe", 30, "johndoe@exmaple.com");

        System.out.println("빌더 적용 전 : " + user1);
    }
}
 

- 빌더패턴 사용 후 객체 생성

package exercise.chapter_60.builder;

public class User {
    private String firstName;
    private String lastName;
    private int age;
    private String email;

    private User(UserBuilder userBuilder) {
        this.firstName = userBuilder.firstName;
        this.lastName = userBuilder.lastName;
        this.age = userBuilder.age;
        this.email = userBuilder.email;
    }

    static class UserBuilder { //inner class
        String firstName;
        String lastName;
        int age;
        String email;

        public UserBuilder() {
        }

        public UserBuilder firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public UserBuilder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }

        public UserBuilder email(String email) {
            this.email = email;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }

}
----------------------------
package exercise.chapter_60.builder;

public class BuilderTest {
    public static void main(String[] args) {
        User user2 = new User.UserBuilder()
                .firstName("John")
                .lastName("Doe")
                .age(30)
                .email("johndoe@exmaple.com")
                .build();

        System.out.println("빌더 적용 후 : " + user2);
    }
}
 

  • 데코레이터 패턴 : 기존 객체 변경 없이 동적으로 기능을 추가, 수정하는 디자인 패턴
public class OrderCoffee {
    public static void main(String[] args) {
        Beverage coffee = new Coffee();
        System.out.println(coffee.getDescription() + ": $" + coffee.cost());

        Beverage coffeeWithMilk = new Milk(coffee);
        System.out.println(coffeeWithMilk.getDescription() + ": $" + coffeeWithMilk.cost());

        Beverage coffeeWithMilkWithSugar = new Sugar(coffeeWithMilk);
        System.out.println(coffeeWithMilkWithSugar.getDescription() + ": $" + coffeeWithMilkWithSugar.cost());

        Beverage coffeeWithCreamAndMilk = new Milk(new Cream(new Coffee()));
        System.out.println(coffeeWithCreamAndMilk.getDescription() + ": $" + coffeeWithCreamAndMilk.cost());
    }
}
 

  • 전략 패턴 : 동적으로 교체가능한 전략을 제공, 객체 관계를 유연하게 만드는 디자인 패턴
public class DiscountCalculator {
    private DiscountStrategy discountStrategy;

    public void setDiscountStrategy(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public double calculateDiscount(double amount) {
        if (discountStrategy != null) {
            return discountStrategy.calculateDiscount(amount);
        } else
            return 0;
    }
}
---------------------------
public class StrategyTest {
    public static void main(String[] args) {
        DiscountCalculator calculator = new DiscountCalculator();

        calculator.setDiscountStrategy(new NewCustomerDiscountStrategy());
        double discount1 = calculator.calculateDiscount(10_000);
        System.out.println("신규 가입자 할인 금액 : " + discount1);

        calculator.setDiscountStrategy(new SeasonDiscountStrategy());
        double discount2 = calculator.calculateDiscount(10_000);
        System.out.println("시즌 할인 금액 : " + discount2);

        calculator.setDiscountStrategy(new ReferenceFriendDiscountStrategy());
        double discount3 = calculator.calculateDiscount(10_000);
        System.out.println("시즌 할인 금액 : " + discount3);
    }
}