본문 바로가기

Programming/국비학원

220517 - 멀티스레드(상태 제어, 데몬스레드), 제네릭(제네릭 타입, 멀티타입파라미터, 제네릭 메소드, 상속 및 구현), 람다식

멀티 스레드
  • 스레드 상태 제어
  • sleep() - 일시 정지

: 실행 중인 스레드 일정 시간 멈춤

하단 참고

 

 

  • yield() - 실행 양보
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
public class ThreadJob1_yield extends Thread{
    
    public boolean stop=false;  //종료 여부
    public boolean work=true;  //작업 진행 여부
    
    @Override
    public void run() {
        while(!stop) {
            if (work) {
                System.out.println("ThreadJob1 일 작업중");
            }else {
                Thread.yield(); //work false => 다른 스레드에게 실행 양보
            }
        }
        System.out.println("ThreadJob1 종료");
    }
    
}
 
 
public class ThreadJob2_yield extends Thread{
    
    public boolean stop=false;  //종료 여부
    public boolean work=true;  //작업 진행 여부
    
    @Override
    public void run() {
        while(!stop) {
            if (work) {
                System.out.println("ThreadJob2 일 작업중");
            }else {
                Thread.yield(); //다른 스레드에게 실행 양보
            }
        }
        System.out.println("ThreadJob2 종료");
    }
    
}
 
 
 
public class YieldEx1 {
 
    public static void main(String[] args) {
        
        ThreadJob1_yield tJob1 = new ThreadJob1_yield();
        ThreadJob2_yield tJob2 = new ThreadJob2_yield();
        
        //2개 스레드 동시 실행
        tJob1.start();
        tJob2.start();
        
        //메인 스레드 2초 동안 일시정지 (1&2 2초) => 작업스레드는 실행
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {}
        
        tJob1.work=false;  //tJob2 스레드만 실행
        
        //메인 스레드 2초 동안 일시정지 (2 2초)
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {}
        
        tJob1.work=true;  //tJob1 스레드 재실행
        
        //메인 스레드 2초 동안 일시정지 (1&2 2초)
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {}
        tJob1.stop = true;
        tJob2.stop = true;
        
    }
 
}
cs

//

1, 2 동시 (2초)

2만 (2초)

1, 2 동시 (2초)

ThreadJob2 종료
ThreadJob1 종료

 

 

  • join() - 다른 스레드의 종료 기다림
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
public class SumThread extends Thread {
    
    private int sum; //getter, setter 필요
 
    public int getSum() {
        return sum;
    }
    
    @Override
    public void run() {
        for (int i=1;i<=100;i++) {
            sum+=i;
        }
    }
    
}
 
 
public class JoinEx1 {
 
    public static void main(String[] args) {
      //작업스레드
        SumThread sumt = new SumThread();  //스레드 생성
        sumt.start();  

//메인스레드
        System.out.print("1~100 합 : "+sumt.getSum());
        
    }
 
}
cs

 

//

1~100 합 : 0  => start하자마자 run & sysout => sum에 아직 값 안 들어올 때 출력

 

 

=> 해결 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class JoinEx1 {
 
    public static void main(String[] args) {
            
        SumThread sumt = new SumThread();  //작업스레드 생성
       sumt.start();  
        try {
            sumt.join();  //메인스레드 일시 정지 (sumt 스레드 실행 끝날 때까지 기다림)
        } catch (InterruptedException e) {
       }


        System.out.print("1~100 합 : "+sumt.getSum());
        
    }
 
}
cs

//
1~100 합 : 5050

 

 

  • wait(), notify()- 스레드 간 협업

공유 객체 => 두 스레드가 작업할 내용을 각각 동기화 메소드로 구분

한 스레드 작업 완료 => notify() => 다른 스레드 일시정지 -> 실행대기로 => wait() => 자신은 일시정지

 

 

wait(long timeout) / wait(long timeout, int nanos)

=> notify() 호출 없이 지정시간 지나면 일시정지 스레드가 실행대기 상태로 자동 변경

 

notifyAll()

=> wait()에 의해 일시정지된 모든 스레드들 실행대기 상태로 변경

cf. notify : 한 개 스레드만 실행대기 상태로 변경

 

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
public class ThreadWorkA extends Thread {
    
    private WorkObject object; //객체 필드 선언
    
    public ThreadWorkA(WorkObject object) { //생성자 -> new 로 생성됨
        this.object=object;
    }
    
    @Override
    public void run() {
        for (int i=1;i<=10;i++) {
            object.methodA();
        }
    }
    
}
 
 
public class ThreadWorkB extends Thread {
    
    private WorkObject object; //객체 필드 선언
    
    public ThreadWorkB(WorkObject object) { //생성자 -> new 로 생성됨
        this.object=object;
    }
    
    @Override
    public void run() {
        for (int i=1;i<=10;i++) {
            object.methodB();
        }
    }
    
}
 
 
//ThreadWorkA, ThreadWorkB의 공유 객체
public class WorkObject {
    //메서드
    public void methodA() {
        System.out.println("ThreadWorkA의 작업 실행");
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
        }
    } 
    //메서드
    public void methodB() {
        System.out.println("ThreadWorkB의 작업 실행");
        try {
            Thread.sleep(300);  //sleep 없으면 너무 빨라서 A B 순차 출력됨
        } catch (InterruptedException e) {
        }
    }
    
}
 
 
public class WorkObjectEx1 {
 
    public static void main(String[] args) {
        //공유 객체 생성
        WorkObject shared = new WorkObject();
        ThreadWorkA workA = new ThreadWorkA(shared);
        ThreadWorkB workB = new ThreadWorkB(shared);
        workA.start();
        workB.start();
        
    }
 
}
cs

 

//
ThreadWorkA의 작업 실행
ThreadWorkA의 작업 실행
ThreadWorkA의 작업 실행
~
ThreadWorkA의 작업 실행
ThreadWorkA의 작업 실행
ThreadWorkA의 작업 실행
ThreadWorkB의 작업 실행
ThreadWorkB의 작업 실행
ThreadWorkB의 작업 실행
~
ThreadWorkB의 작업 실행
ThreadWorkB의 작업 실행
ThreadWorkB의 작업 실행
=> sleep 있으면 섞여서 출력 / 없으면 주로 순차 출력

 

 

=> 협업

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
//ThreadWorkA, ThreadWorkB의 공유 객체
public class WorkObject {
    //메서드
    public synchronized void methodA() {  //동기화 메소드 (A부터 실행됨)
        System.out.println("ThreadWorkA의 작업 실행");
        notify();
        //일시정지->실행대기 => 다른 동기화 메소드에 적용 (각 메소드마다 한번씩 수행하도록)
        try {
            wait();  //ThreadWorkA 일시정지
        } catch (InterruptedException e) {
        }
    } 
    //메서드
    public synchronized void methodB() {
        System.out.println("ThreadWorkB의 작업 실행");
        notify();
        try {
            wait();  //ThreadWorkB 일시정지
        } catch (InterruptedException e) {
        }
    }
    
}
 
 
public class WorkObjectEx1 {
 
    public static void main(String[] args) {
        //공유 객체 생성
        WorkObject shared = new WorkObject();
        ThreadWorkA workA = new ThreadWorkA(shared);
        ThreadWorkB workB = new ThreadWorkB(shared);
        workA.start();
        workB.start();
        
    }
 
}
cs

//
ThreadWorkA의 작업 실행
ThreadWorkB의 작업 실행
ThreadWorkA의 작업 실행
~
ThreadWorkB의 작업 실행
ThreadWorkA의 작업 실행
ThreadWorkB의 작업 실행

 

 

  • stop 플래그, interrupt() - 스레드의 안전한 종료

run() 메소드 모두 실행시 스레드는 자동 종료 / 실행 중인 스레드를 즉시 종료할 때에는 stop 플래그 or interrupt()

 

interrupt() => run() 보유 스레드가 일시정지 상태에 있을 때에만 InterruptedException 발생 => sleep 추가

 

 

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
1. Stop 플래그
 
public class StopThread extends Thread {
 
    private boolean stop;
 
    public void setStop(boolean stop) {
        this.stop = stop;
    }
    
    @Override
    public void run() {
        while(!stop) {
            System.out.println("현재 작업중..");
        }
        System.out.println("자원 정리");
        System.out.println("실행 종료");
    }
    
}
 
public class StopThreadEx1 {
    public static void main(String[] args) {
 
    StopThread stopthread = new StopThread();
    stopthread.start();  
    
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        
    }
    stopthread.setStop(true);
    }
}
//
현재 작업중..
현재 작업중..
현재 작업중..
현재 작업중..
~
현재 작업중..
현재 작업중..
자원 정리
실행 종료
 
 
 
2. interrupt 메소드
 
public class InterruptThread extends Thread {
 
    @Override
    public void run() {
        try {
        while(true) {
            System.out.println("현재 작업 중");
            Thread.sleep(1);
        }
        }catch(InterruptedException e) {} //예제에서 interrupt()로 발생시킴 => while 탈출
        System.out.println("자원 종료");
        System.out.println("실행 종료");
    }
    
}
 
 
public class InterruptThreadEx1 {
 
    public static void main(String[] args) {
 
        InterruptThread thread1 = new InterruptThread();
        thread1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
        thread1.interrupt();  
       //run() 메소드 보유 스레드에서 interruptException 발생시킴
        
    }
 
}
//
현재 작업 중
현재 작업 중
현재 작업 중
현재 작업 중
~
현재 작업 중
현재 작업 중
현재 작업 중
자원 종료
실행 종료
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
public class AutoSaveThread extends Thread {  //Thread.start => run 메소드 자동 실행됨
    
    //저장 (흉내) 메서드
    public void save() {
        System.out.println("작업 내용을 저장");
    }
    
    
    @Override
  public void run() {
       while(true) {
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {}
           save();
       }//while
   }
 
}
 
 
public class AutoSaveThreadEx1 {
 
    public static void main(String[] args) {
 
        AutoSaveThread autoSave = new AutoSaveThread();
        autoSave.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {}
        System.out.println("메인 스레드 종료 !");
        
    }
 
}
//
작업 내용을 저장
작업 내용을 저장
메인 스레드 종료 !
작업 내용을 저장
작업 내용을 저장
작업 내용을 저장
~ 반복
cs

 

=> 해결 : 데몬 스레드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AutoSaveThreadEx1 {
 
    public static void main(String[] args) {
 
        AutoSaveThread autoSave = new AutoSaveThread();
        autoSave.setDaemon(true); //start() 전에
        autoSave.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {}
        System.out.println("메인 스레드 종료 !");
        
    }
 
}
cs

//
작업 내용을 저장
작업 내용을 저장
메인 스레드 종료 !

 

 

제네릭 (컬렉션 프레임워크와 연관) 

타입을 파라미터로 가지는 클래스와 인터페이스

 

타입 파라미터 => 코드 작성 시 구체적인 타입으로 대체되어 다양한 코드 생성시킴

ex. 클래스<타입> / 인터페이스<타입>

cf. 배열 => 고정된 크기
list collection => 크기 고정돼있지 않음

 

 

※ Collections framework - List

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
import java.util.ArrayList;
import java.util.List;  //List 인터페이스 => ArrayList, Vector, LinkedList..
 
public class ListEx1 {
 
    public static void main(String[] args) {
       //동적 배열 : 여러 타입 값들 넣을 수 있음 (참조값)
//but 나중에 값 꺼낼 때 내용값 확인 불가, 강제 캐스팅해야 함
        List list = new ArrayList();  //다형성 
        list.add("홍길동");
        list.add(10);
        list.add(2.6);
        list.add(false);
 
        //String str = list.get(0); //오류 => 인덱스 안 타입, 값 알 수 없어서 값 못 넘겨줌
        String str = (String)list.get(0);  //강제 캐스팅
        int num = (int)list.get(1);  
//매번 내가 스스로 강제 캐스팅 하기 번거로움 -> 제네릭 필요
 
        for (int i=0;i<list.size();i++) {
            System.out.println(list.get(i)); 
        }
        
    }
 
}
//
홍길동
10
2.6
false
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
import java.util.ArrayList;
import java.util.List;
 
public class GenericEx1 {
 
    public static void main(String[] args) {
 
        List<String> list = new ArrayList<String>();  //List Collection 에 제네릭 부여
        list.add("홍길동"); //append(추가)
        list.add("박찬호");  //String만 넣을 수 있음
        list.add("손흥민");
        list.add("이영희");
        list.remove(1); //뒷 숫자들이 빈자리 하나씩 채움
        list.add(1,"이영표"); //insert(중간에 삽입) => 1번 자리 있던 숫자가 뒤로 밀림
        
        String name = list.get(1); //강제캐스팅 X
        System.out.println(name);
        
        for(String str:list) {
            System.out.println(str);  
        }
        
    }
 
}
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
○비제네릭 
 
public class Box {
    //객체 필드
    private Object object; //최상위 클래스 => 모든 것을 받을 수 있음
 
    public Object getObject() {
        return object;
    }
 
    public void setObject(Object object) {
        this.object = object;
    }
    
}
 
 
public class GenericEx2 {
 
    public static void main(String[] args) {
        //비제네릭 
        Box box = new Box();  //인스턴스 객체 생성

        box.setObject("홍길동");
        String name = (String) box.getObject();  //강제 형변환 필수
        
        box.setObject(new Apple());
        Apple apple = (Apple) box.getObject();  //강제 형변환 필수
        
    }
}
 
 
 
○제네릭
 
public class Box<T> {
    //객체 필드
    private T t;
 
    public T getT() {
        return t;
    }
 
    public void setT(T t) {
        this.t = t;
    }
    
}
 
 
public class GenericEx2 {
 
    public static void main(String[] args) {
        
        Box<String> box1= new Box<String>();
        box1.setT("김철수");
        String name = box1.getT();
        
        Box<Integer> box2 = new Box<Integer>();
        box2.setT(50);
        int age = box2.getT();  //강제 형변환 필요 없음
        
    }
 
}
cs

 

 

  • 멀티 타입 파라미터

ex. Class<K,V,..>, interface <K,V,..>

 

 

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
public class Product<T,M> {
 
    private T kind;
    private M model;
 
    public T getKind() {
        return kind;
    }
    public void setKind(T kind) {
        this.kind = kind;
    }
    public M getModel() {
        return model;
    }
    public void setModel(M model) {
        this.model = model;
    }
    
}
 
 
public class ProductEx1 {
 
    public static void main(String[] args) {
        
        Product<Tv, String> product1 = new Product<Tv, String>();
        product1.setKind(new Tv());
        product1.setModel("스마트TV");
        Tv tv = product1.getKind();
        String tvModel = product1.getModel();
        
        Product<Car, String> product2 = new Product<Car, String>();
        product2.setKind(new Car());
        product2.setModel("소나타");
        Car car = product2.getKind();
        String carModel = product2.getModel();
        
    }
 
}
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
public class Util {
    
    public static <T> Box<T> boxing(T t){  //제네릭타입 파라미터 리턴타입 메소드이름(매개변수) 
=> 제네릭 타입 지정, Box클래스로 리턴함, boxing()의 t변수에 매개변수 대입
        Box<T> box = new Box<T>();
        box.setT(t);
        return box;
    }
 
}
 
 
public class UtilEx1 {
 
    public static void main(String[] args) {
 
        Box<Integer> box1 = Util.boxing(50);
        int value = box1.getT();
        
        Box<String> box2 = Util.boxing("김철수");
        String name = box2.getT();
        
    }
 
}
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
public class ChildProduct<T, M, C> extends Product<T, M> {  
//자식클래스는 타입파라미터 추가 가능
 
    private C company;
 
    public C getCompany() {
        return company;
    }
 
    public void setCompany(C company) {
        this.company = company;
    }
    
}
 
 
public class ChildProductEx1 {
 
    public static void main(String[] args) {
 
        ChildProduct<Tv, StringString> product1 = new ChildProduct<Tv, StringString>();
        product1.setKind(new Tv());
        product1.setModel("스마트TV");
        product1.setCompany("삼성");
        
        ChildProduct<Car, StringString> product2 = new ChildProduct<Car, StringString>();
        product2.setKind(new Car());
        product2.setModel("소나타");
        product2.setCompany("현대");
        
    }
 
}
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
public interface Storage<T> {
    //추상메서드
    public void add(T item, int index);
    public T get(int index);
}
 
 
//구현 클래스
public class StorageInter<T> implements Storage<T>{
    //배열타입 필드
    private T[] array;
        
    //생성자
    public StorageInter(int capacity) {  //배열의 크기
        this.array = (T[])(new Object[capacity]);
        //T에 어떤 타입이 올지 모름 => 모든 타입 대입 가능한 Object클래스 사용
 
    }
    
    @Override
    public void add(T item, int index) {
        array[index]=item;
    }
 
    @Override
    public T get(int index) {
        return array[index];
    }
 
}
 
 
public class StorageInterEx1 {
 
    public static void main(String[] args) {
        Storage<String> storage1 = new StorageInter<String>(100);
        storage1.add("홍길동"0);
        storage1.add("김철수"1);
        String name = storage1.get(0);
        
        Storage<Tv> storage2 = new StorageInter<Tv>(50);
        storage2.add(new Tv(), 0);
        Tv tv = storage2.get(0);
        
    }
 
}
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
public interface Person {
    //추상메서드
    public void info();
    
}
 
 
public class PersonEx1 {
 
    public static void main(String[] args) {
        //인터페이스 객체 필드 선언, 익명으로 객체 구현
        Person gildong = new Person() {
            
            @Override
            public void info() {
                System.out.println("나는 종로에 사는 홍길동입니다.");
                System.out.println("자바 웹개발을 배우는 중입니다.");
 
            }
        };
        gildong.info();
 
        //람다식 표현1
        Person chanho=()-> {
            System.out.println("나는 영등포에 사는 찬호입니다.");
            System.out.println("자바 웹개발을 배우는 중입니다.");
        };
        chanho.info();
 
        //람다식 표현2 코드 한 줄 => 중괄호 필요 X
        Person minho=()-> System.out.println("나는 강남에 사는 민호입니다.");
        minho.info();
    
    }
 
}
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
public class ThreadEx5 {
 
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
        
            @Override
            public void run() {
                Toolkit toolkit = Toolkit.getDefaultToolkit(); 
                for (int i=1;i<=5;i++) {
                    try {
                        toolkit.beep(); 
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                    }  
                }//for
            }
        
        });
    thread.start();
        
    }
 
}
 
=>람다식
 
import java.awt.Toolkit;
 
public class ThreadEx5 {
 
    public static void main(String[] args) {
        //람다식 이용 (Runnable 구현 익명 객체)
        Thread thread = new Thread(()-> {
            Toolkit toolkit = Toolkit.getDefaultToolkit(); 
            for (int i=1;i<=5;i++) {
                try {
                    toolkit.beep(); 
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                } 
            }//for
        });
    thread.start();
    
    for (int i=1;i<=5;i++) {
        System.out.println("띵!!");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }  
    }
        
    }
 
}
cs