1. 스레드
2. 스레드 기본 사용법
- 스레드 객체 생성
- 스레드 시작 (start())
- 타겟 (run()) 만들기
package ex19;
// 스레드 기본 (스레드 객체 생성, 스레드 시작, 타켓(run) 만들기)
public class Th01 {
public static void sub1() {
for (int i = 1; i <= 5; i++) {
System.out.println("스레드 1: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void sub2() {
for (int i = 1; i <= 5; i++) {
System.out.println("스레드 2: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) { // CPU -> 메인 스레드
// 람다식 한 줄이면 중괄호 생략 가능. void 타입이면 return 없어도 중괄호 생략 가능.
// run 메서드 = () -> sub1() : Runnable 인터페이스를 통해 run 행위 전달
// 스레드의 제어권 - OS가 가짐
Thread t1 = new Thread(() -> sub1());
t1.start();
new Thread(() -> sub2()).start();
}
}
3. 스레드 객체의 상태 공유
package ex19;
public class Th02 {
static String product = null;
public static void main(String[] args) {
Thread supp = new Thread(() -> {
try {
Thread.sleep(10000);
product = "바나나깡";
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
supp.start();
Thread lis = new Thread(() -> {
while (true) {
try {
Thread.sleep(500);
if (product != null) {
System.out.println("상품이 입고되었습니다. : " + product);
break;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
lis.start();
}
}
4.
package ex19;
// 화가
// 스레드 사용시 대기가 없어지는 건 아님, 사용자 UX가 좋아짐
class MyFile {
public void write() {
try {
Thread.sleep(5000);
System.out.println("파일 쓰기 완료");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
class 화가 {
public void 그림그리기() {
System.out.println("그림 그리기 완료");
}
}
public class Th03 {
public static void main(String[] args) {
MyFile myFile = new MyFile();
화가 painter = new 화가();
// 비동기 프로그램 -> 좋은 UX 가능
painter.그림그리기();
new Thread(() -> myFile.write()).start();
painter.그림그리기();
}
}
5.
package ex19;
import java.util.*;
// 1. 상속 받기
// 2. 상태 지정
// 3. run 재정의해서 사용
class MyTread extends Thread {
List<Integer> list;
@Override
public void run() { // run 재정의해서 target 만들기
for (int i = 0; i < 10; i++) {
System.out.println("MyTread: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
// ArrayList보다 추상적인 List에 의존
public MyTread(List<Integer> list) {
this.list = list;
}
public List<Integer> getList() {
return list;
}
// 컬렉션은 setter보다 add (한 건씩 추가하니까)
public void addList(int num) {
list.add(num);
}
}
// 클래스로 스레드 만들기 (스레드별 상태 보관)
public class Th04 {
public static void main(String[] args) {
// MyTread가 List를 has
// MyTread가 ArrayList에 의존
MyTread t1 = new MyTread(new ArrayList<>());
t1.start();
}
}
6. 스레드 제어
package ex19;
import javax.swing.*;
public class Th05 extends JFrame {
private boolean state = true;
private int count = 0;
private int count2 = 0;
private JLabel countLabel;
private JLabel count2Label;
public Th05() {
setTitle("숫자 카운터 프로그램");
setVisible(true);
setSize(300, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 레이아웃 매니저 설정
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
// 숫자를 표시할 레이블 생성
countLabel = new JLabel("숫자1: " + count);
count2Label = new JLabel("숫자2: " + count2);
countLabel.setAlignmentX(CENTER_ALIGNMENT);
count2Label.setAlignmentX(CENTER_ALIGNMENT);
add(countLabel);
add(count2Label);
// 멈춤 버튼 생성
JButton increaseButton = new JButton("멈춤");
increaseButton.setAlignmentX(CENTER_ALIGNMENT);
add(increaseButton);
// 버튼에 액션 리스너 추가
// while 돌면서 특정한 시간을 두고 멈춤 버튼만 지켜보고 있음
// 감시하고 있는데 버튼 클릭하면 내가 뭘 할지 모르겠어 -> 행위({}) 전달
// 추상 메서드
increaseButton.addActionListener(e -> {
state = false;
});
new Thread(() -> {
while (state) {
try {
Thread.sleep(1000);
count++;
countLabel.setText("숫자1 : " + count);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}).start();
new Thread(() -> {
while (state) {
try {
Thread.sleep(1000);
count2++;
count2Label.setText("숫자2 : " + count2);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}).start();
}
// 스레드 제어 예제
public static void main(String[] args) {
new Th05();
}
}
7. 스레드의 데이터를 리턴 받아서 사용하는 경우
1. 타이밍
- ㅇ
package ex19;
//콜백
class Store implements Runnable {
int qty;
@Override
public void run() {
// 통신 -> 다운로드
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
qty = 5;
}
}
/*
스레드에서 받은 데이터를 리턴 받아서 응용하고 싶을때!!
1. 타이밍 맞추기 (임시방편 - 그래도 쓰는 사람 많음) - 통신에서는 불가
2. 리스너 (부하가 너무 큼)
3. 콜백 (제일 좋음)
*/
public class Th06 {
public static void main(String[] args) {
int totalQty = 10;
Store store = new Store();
Thread t1 = new Thread(store);
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("재고수량 :" + (store.qty + totalQty));
}
}
// 스레드를 쓰는 이유
// 1. 동시에 쓰고 싶을 때
// 2. I/O가 일어날 때 - hdd 접근해서 꺼내서 가져오고~ 오래 걸린다
// I/O = 오래 걸리는 일과 유사
// 느림 -> context switching, 스레드 객체가 계속 만들어짐
2. 리스너
- ㅇ
- 다른일도 하면 스레드 필요하다. 예제 코드에서는 필요하지 x니까~~
package ex19;
//콜백
class Store implements Runnable {
Integer qty;
@Override
public void run() {
// 통신 -> 다운로드
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
qty = 5;
}
}
/*
스레드에서 받은 데이터를 리턴 받아서 응용하고 싶을때!!
1. 타이밍 맞추기 (임시방편 - 그래도 쓰는 사람 많음) - 통신에서는 불가 / CPU가 그냥 쉰다
2. 리스너 (부하가 너무 큼) - CPU가 쉬지 않고 지켜봄
3. 콜백 (제일 좋음)
*/
public class Th06 {
public static void main(String[] args) {
int totalQty = 10;
Store store = new Store();
Thread t1 = new Thread(store);
t1.start();
while (true) {
// store.qty가 int이면 null이 안들어감 -> Integer Wrapper 클래스로 쓰는 것이 낫다
if (store.qty != null) break;
// 너무 빠르게 돌리면 안되니까 sleep
// 금방 끝나지만 너무 많은 연산이 돌아감
try {
Thread.sleep(10);
System.out.println("지켜본다");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("재고수량 :" + (store.qty + totalQty));
}
}
3. 콜백 (중요
- ㄹㅇ
package ex19;
// 콜백
// (1) 콜백 메서드 만들기
// 인터페이스와 함수 하나 만들어서 리턴 받고 싶은 파라미터를 만들어주면 된다.
interface Callback {
void 입고(int gty);
}
class Store implements Runnable {
Integer qty;
Callback callback;
// (2) 리턴이 필요한 곳으로 가서 콜백 메서드 전달받기
public Store(Callback callback) { // 행위 전달 받은 store
this.callback = callback;
}
@Override
public void run() {
// 통신 -> 다운로드
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
qty = 5;
// 끝나면 입고 메서드 호출해주니까 콜백(?
callback.입고(qty); // (3) 종료 시 콜백 메서드 호출
}
}
/*
스레드에서 받은 데이터를 리턴 받아서 응용하고 싶을때!!
1. 타이밍 맞추기 (임시방편 - 그래도 쓰는 사람 많음) - 통신에서는 불가 / CPU가 그냥 쉰다
2. 리스너 (부하가 너무 큼) - CPU가 쉬지 않고 지켜봄
3. 콜백 (제일 좋음)
*/
public class Th06 {
public static void main(String[] args) {
int totalQty = 10;
Store store = new Store(qty -> {
System.out.println("재고수량 :" + (qty + totalQty));
});
Thread t1 = new Thread(store);
t1.start();
}
}
// 스레드를 쓰는 이유
// 1. 동시에 쓰고 싶을 때
// 2. I/O가 일어날 때 - hdd 접근해서 꺼내서 가져오고~ 오래 걸린다
// I/O = 오래 걸리는 일과 유사
// 느림 -> context switching, 스레드 객체가 계속 만들어짐
package ex19;
public class Th01 {
public static void main(String[] args) {
// 모든 스레드는 상태 변수를 공유 -> 동시에 접근하면 교착 상태
//static int num = 5000;
// main thread의 target = main method
// CPU는 Java 실행될 때 main 스레드를 준다.
// 분신 만들기 = 이것만의 새로운 생명 주기를 만드는 것
// main thread가 분신 하나 만들고 start
// Runnable의 run 구현 -> 람다식으로
Thread t1 = new Thread(() -> {
//t1의 생명 주기 = target
// 분신1
for (int i = 0; i < 10; i++) {
System.out.println("분신 1: " + i);
//System.out.println(num);
try {
Thread.sleep(500); // 0.5초
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}); // 이 스레드만의 메인 메서드가 필요 = target
// 메인 스레드가 start를 호출하면 start: os야 남는 스레드 있으면 만들어서 run 좀 호출해줘 (run = call back method
t1.start();
// main thread가 분신 하나 만들고 start
Thread t2 = new Thread(() -> {
// 분신2
for (int i = 0; i < 10; i++) {
System.out.println("분신 2: " + i);
try {
Thread.sleep(500); // 0.5초
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t2.start();
Thread t3 = new Thread(() -> {
// 분신 3
for (int i = 0; i < 10; i++) {
System.out.println("분신 3: " + i);
try {
Thread.sleep(500); // 0.5초
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t3.start();
System.out.println("메인 스레드 종료"); // 실행 -> context switching으로 t1 target 내용 실행되고 ~ => 메인 스레드가 먼저 종료됨 but java는 종료되지 않음
// 분신이 종료되어야 메인 thread도 종료됨
}
}
//인터페이스 메서드 전달 역할도 함 -> 람다로
Share article