멀티 스레드
- 스레드 상태 제어
- 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, String, String> product1 = new ChildProduct<Tv, String, String>();
product1.setKind(new Tv());
product1.setModel("스마트TV");
product1.setCompany("삼성");
ChildProduct<Car, String, String> product2 = new ChildProduct<Car, String, String>();
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 |