예시)


쓰레드의 구현에는 2가지 방법이 존재
Thread클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에 Runnable 인터페이스를 구현하는 방법이 일반적
메인 작업대 외의 작업대는 모두 run()
예시)

ㄴ ② Thread클래스를 상속
ㄴ 쓰레드 총 3개 = 작업대 총 3개 : ① ② ③

ㄴ 출력결과 (병렬적 수행 = 동시에 수행)
ㄴ 호출스택이 각각 있다ㅏㅏ
예시2)

ㄴ 쓰레드 분리가 안됨
ㄴ 왜? start()메서드가 쓰레드 분리(호출스택)해줘야 함
ㄴ run()은 코드만 가져올 뿐 호출스택을 새로 만들지 않음

ㄴ 이제 쓰레드 분리 됨
-> 동시작업
예시)

ㄴ ③ Runnable인터페이스를 구현 -> Thread 생성자 매개변수
ㄴ 쓰레드 총 3개 = 작업대 총 3개 : ① ② ③
참고) 람다식 풀어서보기

ㄴ 출력결과 (병렬적 수행 = 동시에 수행)
ㄴ 호출스택이 각각 있다ㅏㅏ
예시2)

ㄴ 러너블 인터페이스는 run()메서드를 구현만 했을 뿐 쓰레드의 기능은 없음
참고) Interface Runnable
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Runnable.html

ㄴ Runnable 인터페이스의 추상메서드 : run()
ㄴ 함수형 인터페이스 -> 람다식 굳굳

ㄴ Thread클래스 : Runnable 인터페이스의 구현체

ㄴ 2) 방식인 Runnable 인터페이스를 구현하고 Thread 생성자 매개변수로 투입하는 방식
예시)

ㄴ 쓰레드와 러너블 인터페이스 구조도 : implements

ㄴ 현재 인스턴스 변수(멤버변수)로 Runnable target이 있음
-> 생성자 매개변수에 러너블 인터페이스 객체가 들어오면 여기로 할당되게 되있음

ㄴ 생성자 매개변수 : Runnable target
ㄴ 객체가 된 러너블 인터페이스가 매개변수로 들어오게 되면 멤버변수에 있던 Runnable target쪽에 대입

ㄴ Thread클래스의 run()에서 대신 수행
예시) 위 예시 좀 더 간결하게

ㄴ Thread 생성자 매개변수로 러너블 인터페이스 객체가 들어오면 private Runnable target 멤버변수에 대입된다
ㄴ if문 : target = Runnable target
-> 러너블 인터페이스 객체가 들어 왔다 = 값이 있다 = null 이 아님을 의미하여 if문 실행 됨
-> if문 실행코드 : 러너블 인터페이스에 정의된 run()실행 되게끔 하는 코드
ㄴ 근데 이 if문(러너블 인터페이스에 정의된 run()실행)을 Thread클래스의 run()이 실행 시킴
= Thread 클래스의 run()이 Runnable 인터페이스 객체의 run()을 대신 수행한다
예시) 새로운 쓰레드를 생성하고 start()를 호출한 후 호출스택의 변화

예시)

ㄴ 쓰레드 총 2개
참고) 쓰레드
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html
예시) getName()


ㄴ 출력결과
예시2) 생성자 매개변수 통해 이름 변경, getName()
package exam01;
public class Ex01 {
public static void main(String[] args) { // + 호출 스택 -> 메인 쓰레드
Runnable r = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("쓰레드2-" + i);
for(long j = 0; j < 10000000000L; j++);
}
};
Ex01_1 th1 = new Ex01_1();
Thread th2 = new Thread(r);
//th1.run();
//th2.run();
th1.start(); // 호출스택 생성 + run() 메서드 실행
th2.start(); // 호출스택 생성 + run() 메서드 실행
System.out.println("작업 종료!");
}
}
class Ex01_1 extends Thread {
public Ex01_1() {
super("변경된이름1");
}
public void run() {
for (int i = 0; i < 5; i++) {
//System.out.println("쓰레드1-" + i);
System.out.println(getName() + "-" + i);
for(long j = 0; j < 10000000000L; j++);
}
}
}


ㄴ 이름이 바껴서 출력
예시3) static Thread currentThread()

ㄴ 러너블 인터페이스를 구현한 객체를 쓰레드 생성자 함수에 매개변수로 대입하여 쓰레드를 만든 클래스가 Thread클래스에 정의 된 메서드 쓰는 법
시분할 방식
과거엔 하나의 쓰레드로 두개의 작업을 수행한 시간과 두개의 쓰레드로 두 개의 작업을 수행한 시간은 거의 같았다. 오히려 두 개의 쓰레드로 작업한 시안이 싱글쓰레드로 작업한 시간보다 더 걸리게 되는데, 쓰레드 간의 작업 전환(context switching)에 시간이 걸리기 때문이다.
작업전환 (context switching)
우선순위가 높은 경우
-> 시간분할을 더 많이 해서 실행을 더 많이 확보
1~10 : 10에 가까울 수록 우선순위가 높다, 1에 가까울수록 우선순위가 낮다
setPriority(1~10)
-> 요즘은 피씨에 코어가 많아서(멀티코어) 큰 차이가 없다...
참고) 시분할 방식
- 빠르게 번갈아가면서 작업을 하니까 동시에 하는 것 처럼 보인다
예시) getPriority()



boolean isDaemon() // 쓰레드가 데몬 쓰레드인지 확인한다. 데몬 쓰레드이면 true를 반환한다.
void setDaemon(boolean on) // 쓰레드를 데몬 쓰레드로 또는 사용자 쓰레드로 변경한다. 매개변수 on의 값을 true로 지정하면 데몬 쓰레드가 된다.
예시)




interrupt() : 실행 정지 상태인 sleep(), join()를 다시 실행 대기 상태로 변경
interrupt()를 호출
interruptedException 라는 예외 발생됨
-> interruptedException이 발생됨으로서 WAITING상태(일지정지)인 sleep(), join()을 RUNNABLE상태(실행 대기)로 바꿈
interrupted() 는 true로 바뀜
-> interrupt() 실행⭕ : Interrupted() = true
-> interrupt() 실행❌ : Interrupted() = false
isInterrupted() : interrupt() 가 발생했는지 안했는지 상태를 알려줌
-> interrupt() 실행⭕ : isInterrupted() = true
-> interrupt() 실행❌ : isInterrupted() = false
interrupt() 호출
-> 1) isInterrupted()가 true 변경
-> 2) interrupted() 호출, InterruptedException도 발생, isInterrupted로 false 변경
예시)
예시)


ㄴ th1, th2 쓰레드가 완료 된 후 main쓰레드 종료
synchronized
synchronized(this) {
///
}
