티스토리 뷰
📌 230822 쓰레드 정리
✏️ 쓰레드 관련 용어
Process: 프로그램이 메모리에 올라간 상태
Thread: 하나의 프로세스는 하나 이상의 thread를 가지게 되고, 실제 작업을 수행하는 단위는 thread임.
Multi-threading: 여러 쓰레드가 동시에 수행되는 프로그래밍
- 쓰레드는 각각 자신만의 작업 공간을 가진다. (Context)
- 각 쓰레드 사이에서 공유하는 자원(shared resource)이 있을 수 있다. (자바에서는 staticc instance)
- 여러 쓰레드가 자원을 공유하여 작업이 수행되는 경우 서로 자원을 차지하려는 경쟁상태(race condition)이 발생할 수 있다.
- 그래서 한 쓰레드가 자원을 사용하고 있으면, 다른 쓰레드는 접근을 못하게 lock을 해줘야한다.
✏️ 쓰레드의 여러 메소드들
1. priority 우선순위
- 쓰레드는 우선순위를 지정하여 실행시킬 수 있다.
- 우선순위가 높은 쓰레드가 CPU 배분을 받을 확률이 높다.
- 디폴트로는 우선순위 5가 부여된다. (MIN_PRIORITY: 1 MAX_PRIORITY : 10)
- setPriority() / getPriority() 이용하여 사용한다.
2. join()
- 동시에 두개 이상의 Thread가 실행될 때 다른 Thread의 결과를 참조하여 실행해야하는 경우 join() 함수를 사용한다.
- join() 함수를 호출한 Thread가 not-runnable 상태로 된다.
- 다른 쓰레드 의 수행이 끝나면 runnable 상태로 돌아온다.
- main에서 사용하면 main은 쓰레드가 수행이 종료될 때까지 멈추게 된다.
3. interrupt()
- 다른 쓰레드에 예외를 발생시키는 interrupt을 보낸다.
- 쓰레다가 join(),sleep(),wait() 함수에 의해 not-runnable 상태일 때, interrupt() 메서드를 호출하면 다시 runnable 상태가 될 수 있다.
✏️ 쓰레드를 종료하고 싶을 때.
예전에는 stop() 메소드를 사용했지만 요즘에는 잘 사용하지 않는다.
대신 flag 변수를 이용한다.
무한 반복일 경우 while(flag)의 flag 변수 값을 false로 바꾸어 종료를 시킴.
✏️ 멀티 쓰레드 프로그래밍에서의 동기화 문제 해결
Critical section(임계영역): 두 개 이상의 스레드가 공유 자원에 동시에 접근하는 경우 문제가 생길 수 있기 때문에 한 스레드만 접근이 가능한 영역이다. 즉 한 스레드가 독점할 수 있도로 보장된 코드 영역이다.
Semaphore (세마포어) : 임계 영역을 해결하기 위한 방법 중 하나이며, 특별한 형태의 시스템 객체이며 get/release 두개의 기능이 있다.
- 한 순간 오직 하나의 스레드만이 세마포어를 얻을 수 있고, 나머지 스레드들은 대기 (blocking) 상태가 된다.
- 세마포어를 얻은 쓰레드만이 critical section에 들어갈 수 있다.
✏️ 자바에서는 synchronized 메서드나 synchronized 블럭을 사용.
Synchronized 블럭: 현재 객체 또는 다른 객체를 lock으로 만든다.
Synchronized 메소드: 객체의 메소드에 synchronized 키워드를 사용.
- 현재 이 메서드가 속해있는 객체에 lock을 건다.
- 자바에서는 deadlock을 방지하는 기술이 제공되지 않으므로 이 메서드를 사용하면, 다른 synchronized 메소드는 호출하지 않도록 한다.
💡 교착상태 deadlock? 둘 이상의 프로세스가 다른 프로세스가 점유하고 있는 자원을 서로 기다릴 때 무한 대기에 빠지는 상황.
📝 사용 예제 : 은행
은행에서 같은 통장을 공유하고 있는 park, parkWife가 있다.
park은 3000원 저축을, parkWife 는 1000원을 출금하고자 할 때,
둘은 같은 자원을 공유하고 있으므로 동기화 처리를 해줘야한다.
만약 안 해줄 경우, 출금할 때 시간(0.2초)이 저축시간(3초)보다 빠르므로 출금이 먼저 이루어져 값이 이상해진다.
class Bank { //Bank 가 shared resource
private int money = 10000;
//public synchronized void saveMoney(int save) {
public void saveMoney(int save) {
int m = getMoney();
try {
Thread.sleep(3000); //3초
} catch (InterruptedException e) {
e.printStackTrace();
}
setMoney(save+m);
}
public synchronized void minusMoney(int minus) {
int m = getMoney();
try {
Thread.sleep(200); //0.2초
} catch (InterruptedException e) {
e.printStackTrace();
}
setMoney(m - minus);
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
class Park extends Thread {
public void run() {
synchronized (SyncMain.myBank) {
System.out.println("start save");
SyncMain.myBank.saveMoney(3000);
System.out.println("save Money(3000) : " + SyncMain.myBank.getMoney());
}
}
}
class ParkWife extends Thread {
public void run() {
System.out.println("start minus");
SyncMain.myBank.minusMoney(1000);
System.out.println("minus Money(1000) : " + SyncMain.myBank.getMoney());
}
}
public class SyncMain {
public static Bank myBank = new Bank();
public static void main(String[] args) throws InterruptedException {
Park p = new Park();
p.start();
Thread.sleep(200);
ParkWife parkWife = new ParkWife();
parkWife.start();
}
}
실행 결과) 동기화 처리를 안해준 경우, 이상한 값이 나온다.
✏️ wait()/notify() 메서드를 활용한 동기화 프로그래밍
- 리소스가 어떤 조건에서 더이상 유효하지 않을 경우, 리소스를 기다리기 위해 thread가 wait 상태가 된다.
- wait() 상태가 된 thread는 notify()가 호출 될때 까지 기다린다.
- 유효한 자원이 생기면 notify()가 호출되고 wait() 하고 있는 thread 중 무작위로 하나의 thread를 재시작하도록 한다.
- 영원히 호출되지 못한 스레드가 있을 수 있기 때문에, 이럴 경우 notifyAll()을 사용한다.
- notifyAll()이 호출되는 경우 wait() 하고 있는 모든 thread가 재시작된다.
- 이 경우 유효한 리소스인 만큼의 thread만이 수행될 수 있고 자원을 갖지 못한 thread인 경우는 다시 wait() 상태로 만든다.
- 자바에서는 notifyAll() 메소드의 사용을 권장한다.
📝 사용 예제 : 도서관
도서관에 있는 책은 한정적이다.
만약 책이 3권이 있고 그 책을 대출하고자 하는 사람이 5명일 경우, 2명은 먼저 빌려간 3명이 반납할 때까지 기다려줘야한다.
여기서 공유 자원은 책이고, wait() 는 책이 다 대출해가서 0권일 때 걸어준다.
notifyAll()는 빌려간 책이 반납했을 때, 빌리지 못한 대기중인 사람 2명에게 책을 빌릴 수 있다고 알려주는 용도이다. 즉 책을 반납하고자 할 때 notifyAll()를 명시해준다.
class Library { //도서관
public ArrayList<String> shelf = new ArrayList<>();
public Library() {
shelf.add("태백산맥1");
shelf.add("태백산맥2");
}
public synchronized String leadBook() throws InterruptedException {
Thread t = Thread.currentThread();
// if (shelf.size() == 0) { //도서관에 책이 없을 때. wait() X
// return "";
// }
while (shelf.size() == 0) { //도서관에 책이 없을 때.
System.out.println(t.getName() +" waiting start");
wait();
System.out.println(t.getName() +" waiting end");
}
String book = shelf.remove(0);
System.out.println(t.getName() + ": " + book + " lend");
return book;
}
public synchronized void returnBook(String book) {
Thread t = Thread.currentThread();
shelf.add(book);
notifyAll();
System.out.println(t.getName() + ": " + book + " return");
}
}
class Student extends Thread {
Student(String name) {
super(name); //super -> thread
}
public void run() {
try {
String title = LibraryMain.library.leadBook();
if (title == null) {
System.out.println(getName() + " 빌리지 못 했음.");
}
sleep(5000);
LibraryMain.library.returnBook(title);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class LibraryMain {
public static Library library = new Library();
public static void main(String[] args) {
Student st1 = new Student("std1");
Student st2 = new Student("std2");
Student st3 = new Student("std3");
Student st4 = new Student("std4");
Student st5 = new Student("std5");
st1.start();
st2.start();
st3.start();
st4.start();
st5.start();
}
}
실행 결과
'백엔드 공부하기 > TIL' 카테고리의 다른 글
230824 TIL : [Spring Boot] GET, POST, PUT, DELETE 방식 처리 (0) | 2023.08.24 |
---|---|
230823 TIL : HTTP, REST, URI, 스프링 부트란 (0) | 2023.08.23 |
230822 TIL : 직렬화, 역직렬화 (0) | 2023.08.22 |
230817 TIL : 사용자 정의 예외클래스, Logging (0) | 2023.08.17 |
230816 TIL : 입출력 스트림, 스트림 공부, Reduce() 연산 (0) | 2023.08.16 |
- Total
- Today
- Yesterday
- 패스트캠퍼스강의
- #국비지원취업
- 패스트캠퍼스
- 카카오API
- Java
- 그룹스터디워크샵
- 야놀자X패스트캠퍼스부트캠프
- qjzl
- 부트캠프
- 커리어멘토링
- 국비지원캠프
- 야놀자
- 데이터베이스
- boj
- 국비지원
- 백엔드개발자
- 백엔드
- 과정중간회고
- 채팅기능개발
- 국비지원취업
- 백엔드부트캠프
- TiL
- be
- 그룹스터디
- 자료구조
- springboot
- 백준
- 자료구조 #스택 #큐 #덱 #선형자료구조
- 스터디후기
- 프로젝트후기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |