Java

[Java] Thread 스레드 / Multi Thread

소댓 2023. 3. 27. 20:36

[ Thread ] 

- Thread : 하나의 프로그램에서 실행되는 흐름의 단위 

- Multi thread : 동시에 스레드를 진행할 수 있는 것 (ex. 카톡 : 채팅하다가 파일을 전송 가능)
  > 자바는 멀티태스크, 멀티 스레드 환경을 지원

 

* Multi Thread 구현 방법
  1. Thread 클래스 상속
  2. Runnable 인터페이스 구현 : run() override

  3. start() 실행

 

 

- 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
package day18;
 
// CPU, RAM
 
// 컴퓨터가 느린 것 같아
// 1. CPU : 2개 이상
// 2. RAM : 작업공간
// 3. 그래픽카드 : 가상화폐, 채굴..
 
// 메모리를 할당 받아 실행중인 프로그램 ==> 프로세스
 
// 멀티 태스크
// Word 프로세스, 음악..
 
// 하나의 프로그램에서 실행되는 흐름의 단위 : Thread 
 
// 동시에 : Multi thread 
// 카톡 : 채팅하다가 파일을 전송 가능
 
// 자바는 멀티태스크, 멀티 쓰레드 환경을 지원
// Multi Thread 구현 방법
// 1. Thread 상속
// 2. Runnable 인터페이스 구현
 
public class ThreadEx1 {
    private void name() {
        Thread th = Thread.currentThread(); // 현재 실행 중인 쓰레드 객체를 리턴
        
        String name = th.getName(); // 현재 실행 중인 쓰레드의 이름을 리턴
    
        
        System.out.println("현재 실행 중인 쓰레드의 이름은 : "+name);
    }
 
    
}
 
cs

 

 

- Multi 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
package day18;
 
// 멀티 쓰레드
// 1. Thread 클래스 상속
// 2. run() override
// 3. start() 실행
 
public class ThreadEx2 extends Thread{ // Thread 상속
    ThreadEx2(String name) {
        // super();
        super(name); // String 매개변수를 갖는 부모 생성자를 호출
    }
    
    // 부모가 가지고 있는 method를 다시 정의해서 사용 : override
    @Override
    public void run() {
        for(int i = 1; i<=100; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i + " 미터 달리는 중");
            try {
                Thread.sleep((int)(Math.random()*100));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
 
    
    public static void main(String[] args) {
 
        ThreadEx2 th1 = new ThreadEx2("천둥이");
        ThreadEx2 th2 = new ThreadEx2("번개");
        
        // 각 Thread는 독립적
        th1.start(); // 준비가 되면 실행해
        th2.start();
        
        System.out.println(Thread.currentThread().getName() + " main thread end");
        
        
    }
}
 
cs

 

 

- 멀티스레드 : Thread 클래스를 상속받지 못하는 경우 (단일상속만 지원)
  1. Runnable 인터페이스 구현
  2. run() override
  3. 이 객체를 생성
  4. Thread 객체를 생성(생성자로 이 객체) : Thread(Runnable target)
  5. Thread 클래스 객체를 통해서 start() 호출

 

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
package day18;
// 멀티쓰레드 : Thread 클래스를 상속받지 못하는 경우 : (단일상속만 지원)
 
// 1. Runnable 인터페이스 구현
// 2. run() override
// 3. 이 객체를 생성
// 4. Thread 객체를 생성(생성자로 이 객체) : Thread(Runnable target)
// 5. Thread 클래스 객체를 통해서 start() 호출
 
 
 
// 1.
public class ThreadEx3 implements Runnable {
    String name; // 스레드 이름으로 쓰기 위해 선언
    ThreadEx3(String name) {
        this.name = name;
    }
    
    // 2.
    @Override
    public void run() { // run 메서드 오버라이드
        for(int i=1; i<=100; i++) {
            System.out.println(name + " : " + i + "미터 달리는 중");
        }
        
    }
    public static void main(String[] args) { // 메인에서 객체 생성
        // 3.
        ThreadEx3 r1 = new ThreadEx3("토끼");
        ThreadEx3 r2 = new ThreadEx3("거북이");
        
        // 4.
        Thread th1 = new Thread(r1); // 스레드 객체 생성해서 runnable을 매개변수로
        Thread th2 = new Thread(r2);
        // 5.
        th1.start(); // 메인 스레드 실행되고, 준비되면 얘도 실행시킴
        th2.start();
    }
    
}
 
cs

 

 


* 경주 게임 (활용)

 

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
package day18;
 
import java.awt.event.ActionEvent;
 
import java.awt.event.ActionListener;
 
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
 
public class Racing extends JFrame implements ActionListener{
    
    JButton jbtn1, jbtn2, jbtn3, jbtnStart;
    ImageIcon img1, img2;
    Horse h1, h2, h3;
    
    
    Racing() {
            
        setLayout(null);
        
        img1 = new ImageIcon("src/images/Horse.gif");
        img2 = new ImageIcon("src/images/Horsestop.gif");
        
        jbtn1 = new JButton(img2);
        jbtn2 = new JButton(img2);
        jbtn3 = new JButton(img2);
        jbtnStart = new JButton("Start");
        
        
        jbtn1.setBounds(5015012060);
        jbtn2.setBounds(5025012060);
        jbtn3.setBounds(5035012060);
        jbtnStart.setBounds(20065080050);
        
        add(jbtn1);
        add(jbtn2);
        add(jbtn3);
        add(jbtnStart);
        
        jbtnStart.addActionListener(this);
        
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setBounds(1001001200800);
        setVisible(true);
    }
    
    public static void main(String[] args) {
        Racing r = new Racing();
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        
//        System.out.println("레이싱 클래스에서 jbtn1 : "+jbtn1);
        if( h1 == null) {
            h1 = new Horse(jbtn1, img1, img2);
            h2 = new Horse(jbtn2, img1, img2);
            h3 = new Horse(jbtn3, img1, img2);
            
            h1.start();
            h2.start();
            h3.start();
        }
        
        
//        // 1. 버튼의 현재 위치를 가져오기
//        int x = jbtn1.getX();
//        int y = jbtn2.getY();
//        
//        // 2. 버튼의 x 값을 약간 증가
//        x += 3;
//        // 3. 지정된 위치로 버튼을 이동
//        jbtn1.setLocation(x,y);
//        
//        for(int i=0; i<100; i++) {
//            jbtn1.setLocation(jbtn1.getX()+3, jbtn1.getY());
//            jbtn2.setLocation(jbtn1.getX()+3, jbtn2.getY());
//            jbtn3.setLocation(jbtn1.getX()+3, jbtn3.getY());}
//        
//        try {
//            Thread.sleep(5);
//        } catch (InterruptedException e1) {
//            e1.printStackTrace();
//        }
        
}
}
cs

 

 

-  Multi 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
46
47
48
package day18;
 
import java.util.Random;
 
import javax.swing.ImageIcon;
import javax.swing.JButton;
 
// 멀티 쓰레드
public class Horse extends Thread{
 
    JButton jbtn; // Racing의 jbtn 버튼 참조 가능
    
    ImageIcon img1;
    ImageIcon img2;
    
    Horse(JButton jbtn) {
        this.jbtn = jbtn;
//        System.out.println("Horse 클래스 생성자 : "+this.jbtn1);
    }
    
    
    Horse(JButton jbtn, ImageIcon img1, ImageIcon img2) {
        this.jbtn = jbtn;
        this.img1 = img1;
        this.img2 = img2;
    }
    
    @Override
    public void run() {
        // 달리는 말 이미지로 교체
        jbtn.setIcon(img1);
        
        // 동시에 처리할 코드
        Random rnd = new Random();
        for(int i=0; i<100; i++) {
            jbtn.setLocation(jbtn.getX()+rnd.nextInt(20), jbtn.getY());
            try {
                Thread.sleep(50);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 정지해 있는 말 이미지로 교체
        jbtn.setIcon(img2);
    }
    
}
 
cs

 

 


 

* ATM 

 

- 다양한 매개변수 있는 생성자 사용 (source - generate constructor using fields)
  > getter, setter, constructor

 

- synchronized : 동기화 처리 

 

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
package day18;
 
public class ATM {
    String account;
    int balance;
    
    ATM(){ // 기본 생성자
    
    }
    
    // 매개변수 있는 생성자 : source - generate constructor using fields
    public ATM(String account, int balance) { 
        super();
        this.account = account;
        this.balance = balance;
    }
 
    // 다양한 매개변수가 있는 생성자 사용 : getter, setter, constructor
    
    public String getAccount() {
        return account;
    }
    public void setAccount(String account) {
        this.account = account;
    }
    public int getBalance() {
        return balance;
    }
    public void setBalance(int balance) {
        this.balance = balance;
    }
    
    // synchronized : 동기화 처리
    public synchronized void deposit(int money) {
        balance += money;
        System.out.println(money + "원 입금합니다.");
        System.out.println("현재 잔액 : " + balance);
    }
    
    // synchronized : 동기화 처리
    public synchronized void withDraw(int money) {
        if (balance >= money) { // 마이너스 통장 안되게
            balance -= money;
            System.out.println(money + "원 출금합니다.");
        } else {
            System.out.println("잔액이 부족합니다.");
        }
        System.out.println("현재 잔액 : "+balance);
    }
    
    
    
}
 
cs

 

- ATM Test

 

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
package day18;
 
public class ATMTest {
    public static void main(String[] args) {
        
        ATM atm = new ATM("홍길동"10000);
        
        // 5000 입금
        // atm.deposit(5000);
        // 20000 출금
        // atm.withDraw(20000);
        // 12000 출금
        // atm.withDraw(12000);
        
        // Multi Thread로 만들어서 동시에 입금, 출금 하도록 > ATMThread 
        // 동시에 여러 은행 지점에서 이 계좌에
        // 입금, 출금을 동시에 처리한다는 가정
        
        ATMThread at1 = new ATMThread(atm);
        ATMThread at2 = new ATMThread(atm);
        ATMThread at3 = new ATMThread(atm);
        
        // 동기화 처리 > 작업을 한 명씩 하게 하는 것..이 필요
        // 동기화 : 데이터 베이스의 lock
        // 동기화하면 느려진다..는 단점이 있음 (lock이 걸리기 때문)
        
        
        at1.start();
        at2.start();
        at3.start();
        
        
        
    }
}
 
cs

 

 

- ATM Multi 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
package day18;
 
import java.util.Random;
 
// 동시에 하나의 계좌에 입금과 출금 기능을
// 수행할 수 있도록 멀티 쓰레드 구현
 
public class ATMThread extends Thread {
    
    ATM atm;
 
    public ATMThread(ATM atm) {
        super();
        this.atm = atm;
    }
    
    @Override
    public void run() {
        Random rnd = new Random();
        // 5회 입금 번갈아 가면서 5회 출금
        
        boolean flag = true;
        
        for(int i = 0; i<10; i++) {
            int money = rnd.nextInt(10)*1000;
            
            if(flag) {
                atm.deposit(money);
            } else {
                atm.withDraw(money);
            }
            
            flag = !flag; // 참이면 거짓으로, 거짓이면 참으로 만들고 할당
            
        } 
        // 랜덤하게 입.출금 
        
        
    }
 
}
 
cs

 

 


 

* Car(차고)

 

- 차고 생성 > ArrayList

 

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
package day18;
 
import java.util.ArrayList;
import java.util.Random;
 
public class Car {
    // 차고
    ArrayList<String> list;
    String[] carNamelist = {"벤틀리""그랜져""벤츠""폭스바겐""붕붕카"};
    Car() { // 기본생성자 (차고)
        list= new ArrayList<String>();
    }
    
    // 자동차 이름을 얻어오기 getCarName()
    public String getCarName() {
        Random rnd = new Random();
        // rnd.nextInt(5); 0~4까지 수 리턴
        // int pos = rnd.nextInt(5);
        // carNameList[pos] : 랜덤한 위치의 포지션을 갖는 배열의 요소
        return carNamelist[rnd.nextInt(5)]; // 내가 가진 차 목록 중 한 개를 랜덤하게 추출
    
    }
    
    // 차량을 생성해서 차고에 넣는 메서드 push();
    public synchronized void push(String car) {
        System.out.println("현재 차고 상태 : "+list);
        System.out.println("자동차가 생성되었습니다. : "+car);
        list.add(car);
        // 자고 있는 고객이 있다면 깨워줌
//        this.notify(); // 한 명만 깨움
        this.notifyAll();
    }
    
    public synchronized String pop() { // 가장 마지막에 있는 차를 꺼내서 출력
        String carName = null;
        
        System.out.println("pop() : 현재 차고 상태 "+ list);
        
        //차가 있으면 출고
        if(list.size() == 0) {
            System.out.println("차고에 차량이 없습니다. 잠시 기다리세요.");
        // 없으면 기다려
        try {
            this.wait(); // 쿨쿨.. 반드시 synchronized 메서드여야 함
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        }
    
        carName = list.remove(list.size() -1);
        System.out.println("자동차가 출고 되었습니다."+carName);
        return carName;
    }
    
}
 
cs

 

- 차량 생산

 

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 day18;
 
public class Producter extends Thread { // 쓰레드 상속 -> 멀티쓰레드 하기 위해
    
    private Car car;
    
    public Producter(Car car) {
        super();
        this.car = car;
    }
 
    @Override
    public void run() {
        // 차량을 20대를 2초마다 1대씩 생산
        for(int i=0; i<20; i++) {
            // 2초 잠들기 > Thread.sleep(2000)하고, 클릭해서 추가
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // 차량 이름을 얻어오기
            String name = car.getCarName();
            // 차고에 넣기
            car.push(name);
        }
    }
}
 
cs

 

 

- 차량 구매

 

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
package day18;
 
import java.util.Random;
 
public class Customer extends Thread{
 
    Car car;
 
    public Customer(Car car) { // 생성자
        super();
        this.car = car;
    }
    
    @Override
    public void run() {
        // 20대 차량 구매
        Random rnd = new Random();
        for(int i=0; i<20; i++) {
            try { // surround with try/catch
                Thread.sleep(rnd.nextInt(5*1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            // 1대씩 꺼내기 : 구매
            String carName = car.pop();
            
        }
    }
 
    
 
 
    
}
 
cs

 

 

- 메인 메서드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package day18;
 
public class ProductCustomerEx {
 
    public static void main(String[] args) {
    
        Car car = new Car();
        
        // 자동차 생산
        Producter p = new Producter(car);
        
        // 자동차 소비
        Customer c = new Customer(car);
        
        p.start();
        c.start();
        
    }
}
 
cs