Post

MultiThread

✅ Java의 Thread

Process: 실행되는 모든 프로그램, CPU위에서 돌아가고 있는 프로그램
Thread: Process 내 동시에 진행되는 작업 갈래
여러 요리사가 동시에 요리를 처리하기

  • 병행처리
  • 병렬처리

single thread

Main Thread 시작
main method
순차적으로 실행

multi thread

running more than two threads at once

마찬가지로 Main Thread에서 시작
병렬 실행
실행되는 순서는 매번 달라진다.

☑️ Java Thread 작업 순서

1. 스레드 객체 생성 new

java에서는 Thread도 클래스

2. 실행 대기 Runnable

Runnable 함수형 interface 사용
Runnable 상속받은 클래스 구현 및 사용
lambda 또는 anonymous로 사용도 가능하다.

3. 실행

4. 종료 Terminated

종료되면 GC가 치움

✅ MultiThread Programming

☑️ Java Thread 생성, start

how to create thread

  1. runnable interface: make class into isntance, argument
  2. thread class inherence: run(), no need to override
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
//use method 1: runnable interface
public class JavaThreading {
    public static void main(String[] args) {
        //thread생성
        Thread thread1= new Thread(new MyRunnable());
        Thread thread2= new Thread(new MyRunnable());
        System.out.println("main Thread name: " + Thread.currentThread().getName()+ " is running"); //main Thread name: main is running

        //start thread
        thread1.start();
        thread2.start();

        System.out.println("main Thread name: " + Thread.currentThread().getName()+ " is running");

    }

    //runnable
    static class MyRunnable implements Runnable{
        @Override
        public void run(){
            //get current thread name
            System.out.println("class MyRunnable Thread name: " + Thread.currentThread().getName()+ " is running");

            // class MyRunnable Thread name: Thread-0 is running
            // class MyRunnable Thread name: Thread-1 is running
        }
    }
}

☑️ Thread can be anonymous, Lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class JavaThreading {
    public static void main(String[] args) {
        //⭐️ thread생성
        Thread thread1= new Thread(new MyRunnable());
        //⭐️ anonymous thread
        Thread thread2= new Thread(new MyRunnable() {
            @Override
            public void run() {
                System.out.println("Thread name: "+Thread.currentThread().getName()+" is running");
        }
        });
        //⭐️ Lambda
        Thread thread3= new Thread(()-> System.out.println("Thread name: " + Thread.currentThread().getName()+ " is running"));

        //start thread
        thread1.start();
        thread2.start();
        thread3.start();

        System.out.println("Thread name: " + Thread.currentThread().getName()+ " is running");
    }
}

☑️ thread는 순서 정해진 것 없음

순서 보장되는 것 없음, 아주 무작위
run(): one call stack in main()
start(): make call stack for thread, contect swithcing

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
package chap59_MultiThread;

public class MultiThreadingExample {
    public static void main(String[] args) {
        //1에서 100까지 multiThreading으로 출략

        //thread 2개
        Thread thread1= new Thread(new PrintNumRunnable(1, 50));
        Thread thread2= new Thread(new PrintNumRunnable(51, 100));

        thread1.start();
        thread2.start();

        //thread 3개
        Thread thread3= new Thread(new PrintNumRunnable(1, 30));
        Thread thread4= new Thread(new PrintNumRunnable(31, 60));
        Thread thread5= new Thread(new PrintNumRunnable(61, 100));

        thread3.start();
        thread4.start();
        thread5.start();

    }
}
//▶️ 출력 결과
//1에서 50까지, 51에서 100까지 출력되는 것 ❌
//Thread는 순서는 무관하게 출력되기 때문에 1~100까지의 숫자가 마구잡이로 출력된다.
//순서가 아주 마구잡이로 출력
//만약 Thread가 한 개였다면 1~100까지 차례대로 출력되었을 것이다.

✅ 개별 영역과 공통 영역, 동기화 문제, Syncrhonize

스크린샷 2023-12-29 오후 5 29 30

✔️ 개별 영역:

혼자 먹는 자장면
PC register, Stack Area, Native Method Stack

✔️ 공통 영역:

같이 공유해서 먹는 탕수육
Method Area, Heap Area

💥 thread의 동기화 문제

thread 순서가 정해지지 않아 발생하는 문제
공통 영역에 여러 개의 thread가 동시에 접속해서 수정하기 시작하면, 충돌/누락 문제 발생 가능

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
44
45
//Counter.java
//1씩 커지는 메소드
public class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

//IncrementRunnable.java
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++) {
            counter.increment();
            System.out.println("Count: " + counter.getCount());
        }
    }
}

//MultiThreadIssue.java
public class MultiThreadIssue {
    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();
    }
}
// 하나의 counter에 대해서 thread 여러개 실행하니 3000까지 출력이 안 되고 누락이 발생할 수 있다.

🚪 thread의 동기화 해결 Syncrhonize

thread의 순서는 바꿀 수 없으니
thread의 영역 정해주기
먼저 수정하고 있는 thread가 그 영역 수정 끝나면, 그 다음 thread가 또 수정하고…
Syncrhonize는 일정 영역의 문을 만들어서, 한 번에 한 개의 thread만 들어와서 수정 가능
나머지 thread는 문 밖에서 기다려야 함, 그 영역에 접근할 수 없음

  • critical section: section that all threads share
  • lock: only one thread can have permission to lock critical section

  • wait(): one thread has lock. wait.
  • notify(): give permission to lock to waiting thread, let the thread run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// package chap59_MultiThread;

// public class Counter {
//     private int count = 0;

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

//     public int getCount() {
//         return count;
//     }
// }

✅ Server MultiThread

Server는 다수의 client의 요청을 동시에 처리해야 한다.
Server는 client의 요청을 하나하나 처리하는게 아니라, 동시에 조금조금씩 처리하고 싶음 ➡️ multiThread

기존의 server코드 개선하기
multi thread하기 전에는 모두 main thread 안에서 실행되고 있음
이제 main thread는 socket 열고 끝.
다른 multi thread에서 data가져와서 list에 추가하기

또 list는 공통영역이니까 synchronize 해주기

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// ✅ main thread
public class ServerInfinite {
    public static void main(String[] args) {
        List<Customer> customerList= new ArrayList<>(); //고객 대기 리스트

        // try(ServerSocket serverSocket= new ServerSocket(1234);
        // ){
        //     System.out.println("server started");

        //     while(true){
        //         try{
        //             Socket clientSocket= serverSocket.accept();

                    //💡 multiThread
                    //main thread는 socket열고 끝
                    //다른 thread에서 input, output받도록 runnable로 넘겨버림!
                    Thread request= new Thread(new RequestHandlerRunnable(clientSocket, customerList));
                    request.start();

//                     } catch(IOException e){
//                    e.printStackTrace();
//             }
//             }
//         }catch(IOException e){
//             e.printStackTrace();
//         }
//     }
// }

// ✅ multi thread
//multi thread에서 추가하는 기능 구현
public class RequestHandlerRunnable implements Runnable{

    private Socket clientSocket;
    private List<Customer> customerList;

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

    @Override
    public void run(){
        System.out.println("client 접속하였습니다.");
try {
    InputStream clientInputStream = clientSocket.getInputStream();
    ObjectInputStream objectInputStream = new ObjectInputStream(clientInputStream);

    OutputStream serverOutputStream = clientSocket.getOutputStream();
    PrintWriter printwriter = new PrintWriter(serverOutputStream, true);

    //직렬화
    Customer customer = (Customer) objectInputStream.readObject();
    //💡customerList는 공통영역 ➡ synchronize 필요
    //customerList.add(customer); ❌
    ListUtil.addList(customerList, customer);

    // System.out.println("Thread name: " + Thread.currentThread().getName());
    // System.out.println(customer + " added to Waiting List");

    // printwriter.println("Customer waiting list: " + customerList);

    //socket 닫기
    clientSocket.close();
// } catch(Exception e){
//     e.printStackTrace();
// }
//     }
// }

// ✅ client.java
//수정한 내용이 없음
public class Client {
    // public static void main(String[] args) {

    //     try(Socket socket= new Socket("localhost", 1234)){

    //         OutputStream outputStream= socket.getOutputStream();
    //         ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

    //         InputStream inputStream = socket.getInputStream();
    //         BufferedReader bufferedReader= new BufferedReader(new InputStreamReader(inputStream));

    //         Customer customer= new Customer("ID111", "Park");

    //         objectOutputStream.writeObject(customer);
    //         objectOutputStream.flush();

    //         String response= bufferedReader.readLine();
    //         System.out.println("Customer List from Server"+ response);

    //         System.out.println("Server finished");
    //     } catch (UnknownHostException e){
    //         e.printStackTrace();
    //     } catch(IOException e){
    //         e.printStackTrace();
    //     }
    // }
}
// ✅  synchronized 구현하기 위한 ListUtil.java
public class ListUtil {
    public synchronized static void addList(List<Customer> customerList, Customer newCustomer){
        customerList.add(newCustomer);
    }
}
//synchronized하기 위해 ListUtil추가한 이유는 customerList의 경우, 내가 만든 list가 아니기 때문
This post is licensed under CC BY 4.0 by the author.