# 스레드 (Thread)
- 스레드가 무엇인지 설명할 수 있다.
- 싱글 스레드와 멀티 스레드의 차이를 설명할 수 있다.
- 스레드를 생성하는 두 가지 방법을 활용할 수 있다.
- 스레드를 실행하고 동기화 할 수 있다.
- 스레드의 상태를 이해하고 제어할 수 있다.
# 자바 가상 머신 (Java Virtual Machine)
- JVM이 무엇인지 알고, 메모리 구조를 개괄적으로 이해한다.
- 가비지 컬렉션이 무엇인지 이해한다.
1. 스레드(Thread) 란?
- 프로세스(Process)와 스레드(Thread)
→ 스레드는 데이터와 애플리케이션이 확보한 자원을 활용하여 소스 코드를 실행함 (하나의 코드 실행 흐름)
- 메인 스레드 (Main Thread)
→ 다른 스레드 없이 메인스레드만 가지고 있는 애플리케이션은 싱글스레드 프로세스 라고 함
- 멀티 스레드 (Multi - Thread)
2. 스레드의 생성과 실행
- 작업 스레드 생성과 실행
▪ Runnable 인터페이스를 구현한 객체에서 run()을 구현하여 스레드를 생성하고 실행하는 방법
package thread;
public class threadExample1 {
public static void main(String[] args) {
// Runnable 인터페이스를 구현한 객체 생성
Runnable task1 = new ThreadTask1();
// Runnable 구현 객체를 인자로 전달하면서 Thread 클래스를 인스턴스화 하여 스레드 생성
Thread thread1 = new Thread(task1);
// 위의 두 줄을 아래와 같이 한 줄로 축약할 수 있음
//Thread thread1 = new Thread(new ThreadTask1());
// 작업 스레드를 실행시켜, run() 내부의 코드 처리
thread1.start();
// 반복문 추가
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
// Runnable에는 run()이 정의되어 있기 때문에 반드시 구현해야 함
class ThreadTask1 implements Runnable {
// run()메서드 바디에 스레드가 수행할 작업 내용 작성
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
// 출력 결과
@@@@@@@@@@@@@@@@@@@@@################@@@@@@@@@####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@####
@@@@@@@@@@@@@@@@@@@@@@@@############################################################################
▪ Thread 클래스를 상속받은 하위 클래스에서 run()을 구현하여 스레드를 생성하고 실행하는 방법
package thread;
public class ThreadExample2 {
public static void main(String[] args) {
ThreadTask2 thread2 = new ThreadTask2();
thread2.start();
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
// Thread 클래스를 상속받는 클래스 작성
class ThreadTask2 extends Thread {
// run()메서드 바디에 스레드가 수행할 작업 내용 작성
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
}
//출력 결과
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###############################################@@@#######
####################################@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##########
→ 두가지 방법 모두 같은 결과를 냄
- 익명 객체를 사용하여 스레드 생성하고 실행하기
클래스를 따로 정의하지 않고도 익명 객체를 활용하여 스레드를 생성하고 실행시킬 수 있음
▪ Runnable 익명 구현 객체를 활용한 스레드 생성 및 실행
package thread;
public class ThreadStudy {
public static void main(String[] args) {
// 익명 Runnable 구현 객체를 활용하여 스레드 생성
Thread thread1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("#");
}
}
});
thread1.start();
for (int i = 0; i < 100; i++) {
System.out.print("@");
}
}
}
3. 스레드의 이름
- 스레드 이름 조회
: 메인스레드 외에 추가로 생성한 스레드는 기본적으로 "Thread-n" 이라는 이름을 가짐
public class ThreadExample3 {
public static void main(String[] args) {
Thread thread3 = new Thread(new Runnable() {
public void run() {
System.out.println("Get Thread Name");
}
});
thread3.start();
System.out.println("thread3.getName() = " + thread3.getName());
}
}
// 출력 결과
Get Thread Name
thread3.getName() = Thread-0
Process finished with exit code 0
→ " 스레드참조값.getName() " 으로 스레드의 이름 조회 가능
- 스레드 이름 설정
public class ThreadExample4 {
public static void main(String[] args) {
Thread thread4 = new Thread(new Runnable() {
public void run() {
System.out.println("Set And Get Thread Name");
}
});
thread4.start();
System.out.println("thread4.getName() = " + thread4.getName());
thread4.setName("Code States");
System.out.println("thread4.getName() = " + thread4.getName());
}
}
// 출력 결과
Set And Get Thread Name
thread4.getName() = Thread-0
thread4.getName() = Code States
Process finished with exit code 0
→ " 스레드참조값.setName() " 으로 스레드의 이름 설정 가능
- 스레드 인스턴스의 주소값 얻기
public class ThreadExample1 {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
thread1.start();
System.out.println(Thread.currentThread().getName());
}
}
// 출력 결과
main
Thread-0
Process finished with exit code 0
→ " System.out.println(Thread.currentThread().getName()); " 으로 스레드의 이름 조회 가능
4. 스레드의 동기화
- 스레드 동기화 란?
: 멀티 스레드 프로세스는 두 스레드가 같은 데이터를 공유하게 되면서 발생하는 문제를 해결하기 위한 방법
▪ try { Thread.sleep(1000); } catch (Exception error) {}
: Thread.sleep(1000);
→ 스레드를 일시정지 시키는 메서드 (어떤 스레드가 일시정지되면 대기열에서 기다리던 다른 스레드가 실행됨
→ Thread.sleep()은 반드시 try-catch 문의 try 블록 내에 작성해야 함
→ 1000 == 1초
▪ try{} catch(){}
: 예외 처리에 사용되는 문법
→ try 블록 내의 코드를 실행하다가 예외 또는 에러가 발생했을 때 catch 블록 내의 내용을 실행
public class ThreadExample3 {
public static void main(String[] args) {
Runnable threadTask3 = new ThreadTask3();
Thread thread3_1 = new Thread(threadTask3);
Thread thread3_2 = new Thread(threadTask3);
thread3_1.setName("김코딩");
thread3_2.setName("박자바");
thread3_1.start();
thread3_2.start();
}
}
class Account {
// 잔액을 나타내는 변수
private int balance = 1000;
public int getBalance() {
return balance;
}
// 인출 성공 시 true, 실패 시 false 반환
public boolean withdraw(int money) {
// 인출 가능 여부 판단 : 잔액이 인출하고자 하는 금액보다 같거나 많아야 합니다.
if (balance >= money) {
// if문의 실행부에 진입하자마자 해당 스레드를 일시 정지 시키고,
// 다른 스레드에게 제어권을 강제로 넘깁니다.
// 일부러 문제 상황을 발생시키기 위해 추가한 코드입니다.
try { Thread.sleep(1000); } catch (Exception error) {}
// 잔액에서 인출금을 깎아 새로운 잔액을 기록합니다.
balance -= money;
return true;
}
return false;
}
}
class ThreadTask3 implements Runnable {
Account account = new Account();
public void run() {
while (account.getBalance() > 0) {
// 100 ~ 300원의 인출금을 랜덤으로 정합니다.
int money = (int)(Math.random() * 3 + 1) * 100;
// withdraw를 실행시키는 동시에 인출 성공 여부를 변수에 할당합니다.
boolean denied = !account.withdraw(money);
// 인출 결과 확인
// 만약, withraw가 false를 리턴하였다면, 즉 인출에 실패했다면,
// 해당 내역에 -> DENIED를 출력합니다.
System.out.println(String.format("Withdraw %d₩ By %s. Balance : %d %s",
money, Thread.currentThread().getName(), account.getBalance(), denied ? "-> DENIED" : "")
);
}
}
}
5. 스레드의 상태와 실행 제어
- 작업 스레드 생성과 실행
1. apt 명령어
- 패키지 설치
: sudo apt install 설치할패키지
↓ 이전 글 ↓
↓ 코트스테이츠 부트캠프 관련 글 한번에 보기 ↓
[코드스테이츠] 05_10_TIL : 알고리즘 / 자료구조 _ 재귀함수 이론 (0) | 2023.05.10 |
---|---|
[코드스테이츠] Section 1 회고 _ 04.11 ~ 05.09 (0) | 2023.05.09 |
[코드스테이츠] 05_04_TIL : I/O, 스트림 심화 (0) | 2023.05.04 |
[코드스테이츠] 05_03_TIL : 애너테이션, 람다, 스트림 (1) | 2023.05.03 |
[코드스테이츠] 05_01_TIL : 컬렉션 (Collection) (0) | 2023.05.01 |