실제 작업을 하는 곳
cpu는 쓰레드하고만 이야기
쓰레드는 실제로 작업을 하는 것
프로세스의 자원을 이용해서 데이터, 메모리, 자원등을 이용해서 실제 작업을 수행하는 것
cpu를 사용하는 최소 단위
#멀티쓰레드
1프로세스
다중 쓰레드
#
쓰레드보다 코어가 작을 경우
쓰레드를 잘게 쪼개서, 라운드로빙방식(RR방식)
쓰레드보다 코어가 많을 경우
#
JVM - main - mainThread -
#
다른 클래스에 존재하는 메소드를 간단히 사용하는 법 import static을 활용해서 사용한다.
throws //s자 - 던진다. 나를 던진고
자기를 호출한 곳에서 인터럽트인셉션을 떠넘긴다.
thow // s자 안붙음
이 뒤에 있는 곳에 예외를 만들어서 실행해라.
스레드생명주기
5. 쓰레드의 동기화
- 임계영역(critical section) : 다수의 쓰레드가 공유 자원을 참조하는 코드 영역
- 동기화 : 다수의 쓰레드가 공유 자원을 충돌 없이 사용할 수 있도록 공유 자원에
배타적이고 독점적으로 접근할 수 있는 방법
- 임계영역을 동기화 시키는 방법
.메소드에 synchronized 키워드를 지정
public synchronized void 메소드(){ 임계영역 코드 }
. 코드의 일부에 synchronized 키워드를 지정
synchronized(공유객체) { 임계영역 코드 }
6. 대기와 통보, 생명주기
쓰레드 스케줄링 : jvm은 쓰레드의 개수, 쓰레드의 상태, 우선순위 등 쓰레드와 관련된 모든 정보를 관리
쓰레드 객체를 생성하고 start() 메소드를 호출해서 jvm에게 해당 쓰레드를 스케줄링
해달라고 요청
생명주기 : start()메소드를 호출하면 쓰레드는 실행 대기 상태가 되고 -> jvm이 실행대기 상태에
있는 쓰레드 중에서 하나늘 실행 상태로 만들어 run()메소드를 실행
실행 상태의 메소드는 실행 도중에 다시 실행 대기 상태로 돌아갈 수도 있고 실행을
종료할 수도 있음, 또 실행을 일시 중시하는 상황도 있을 수 있음
일시 정지로 보냄 : sleep(long millis) - 주어진 시간 동안 쓰레드를 일시 정지 상태로
만든다. 주어진 시간이 지나면 자동적으로 실행 대개 상태가 됩니다.
: join () - join()메소드를 호출한 thread는 일시 정지 상태가 된다.
실행 대기 상태가 되려면, join(0 메소드를 가진 쓰레드가
종료되어야 합니다.)
: wait() - 동기화 블록 내에서 thread를 일시 정지 상태로 만든다.
(동기화 - 나 하고 있으면 너 멈춰야되, 일방해하지마)
(비동기화 - 내가멈추면 너도 멈춰야된다.
내가 하면 너도 해야되,같이일하자)
일시 정지에서 벗어나기 : interrupt() - 응급상황 일시정지 상태일 경우,
interruptedException를 발생시켜 실행 대기 상태 또는 종료 상태로 만든다.
: notify() - wait()메소드로 인해 일시정지 상태인 thread를 실행 대기
: notifyAll() - 위의 문장과 동일
실행 대기로 보냄 : yield() - 실행 상태에서 다른 thread에게 실행을 양보하고
실행 대기 상태가 됩니다.
(thread에 속하지 않고)Object에 속하다 wait, notify, notifyAll
ex) 요리사(Cook)와 손님(Customer)의 관계 - 쓰레드의 협업
요리(Dish)는 5개를 만들어 제공할 예정, 손님도 5가지의 요리를 먹을 것임
요리한 음식이 없을 때는 요리사가 요리를 해야 고객이 음식을 먹을 수 있고,
한 번에 한 접시씩 요리사와 고객 사이에 오고 간다고 가정
음식은 공유 자원이 되고, 요리사와 고객은 쓰레드
고객은 음식을 먹은 후 요리사에게 통보(notify())해야 하고, 요리된 음식이 없다면 대기(wait())해야 함
요리사는 음식을 요리 한 후 고객에게 통보(notify()) 해야하고, 요리된 음식을 고객이 먹고 있다면 대기(wait())
wait()
notify()
notifyAll()
7. 쓰레드 풀(thread pool)
- 동시에 실행하는 쓰레드의 개수 제한(thread 개수가 많아지면 그에 따른 thread 객체 생성과 스케줄링 등으로 cpu와 메모리에 많은 부하가 발생할 수 있음)
- 제한된 개수의 쓰레드를 JVM이 관리하도록 맡기는 방식
- 개발자가 쓰레드를 생성할 필요가 없음
실행할 작업을 쓰레드 풀에 전달하면 JVM이 쓰레드 풀의
유휴 쓰레드중 하나를 선택해서 쓰레드를 실행
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute("쓰레드객체");
맨 마지막에 exec.shutdown();
void shutdown() : 현재 처리 중인 작업과 남아 있는 모든 작업을 처리한 후 쓰레드 풀 종료
List<Runnable> shutdownNow() : 현재 처리 중인 작업을 중지시키고 쓰레드 풀을 종료
◐.지난예제
public class HellJavaThread implements Runnable { //exteds Thread
@Override
public void run() {
for(int i = 0 ; i <= 5 ; i++) {
//sleepShow(500); 메소드를 활용해서, 처리 할 수 있다.
try { //지연 시간
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("예외 발생");
}
System.out.print("Hello!! ");
}
}
//Thread는 Runnable을 구현하고 있다.
//throws - 예외를 던져주었다.
public static void sleepShow(long num) { //밀리초라서, long형을 받음
//클래스명.메소드를 입력하면, 다른곳에서도 사용가능하다.
//인스턴스의 영역과 스타틱 영역을 구분하는 것이 필요하다.
try {
Thread.sleep(num);
} catch (InterruptedException e) {
System.out.println("예외 발생");
}
}
}
public class HelloJavaThreadMain {
public static void main(String[] args) {
System.out.println("** main thread **");
//# Thread 객체 생성
Runnable h = new HellJavaThread();
Thread ht = new Thread(h);
ht.setDaemon(true); //(보조 쓰레드)데몬 쓰레드로 설정 , start메소드보다 앞에 있어야한다.
//# Thread 실행
ht.start();
//# main thread 작업
for(int i = 1 ; i <= 5; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("예외 처리");
}
System.out.println(" java ");
}
System.out.println("** main thread 종료 **");
}
}
◐.스레드풀
public class ThreadPoolThreadMain {
public static void main(String[] args) {
System.out.println("** 메인 부분 실행... **");
Thread runableThreadPool = new Thread(new Runnable() {
@Override
public void run() { //익명구현객체
for(int i = 1 ; i <= 5; i++) {
System.out.println("안녕!! ");
try {
Thread.sleep(1000); //예외를 던져놔서 여기서 처리해야한다.
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//try ~ catch
}//for
}//run
});
//# Thread Pool 생성 - 내가 하는 것 아니라 JVM이 스타트한다.
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(runableThreadPool);
//# 메인 부분 작업
System.out.println("** main **");
for(int i = 1; i <= 5; i++) {
System.out.println("즐거운 시간~~ ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("예외 발생");
}
}
System.out.println("** 메인 끝 **");
//# Thread pool 종료
exec.shutdown(); // 이것이 호출 되지 않으면 종료가 되지 않습니다.
}
}
◐.쓰레드의 ‘join’의 사용법
public class CalcThread extends Thread {
private int sum;
public int getSum() {
return sum;
}
public void setSum(int sum) {
this.sum = sum;
}
@Override
public void run() {
for(int i = 1; i<= 100; i++) {
sum += i;
}
}
}
public class CalcThreadMain {
public static void main(String[] args) {
//tread를 사용법
//음악플레이어 - 재생 및 일시정지
/*
System.out.println("** main **");
CalcThread cal = new CalcThread();
cal.start();
System.out.println("--------------");
System.out.println("1-100까지의 합 : "+ cal.getSum()); // o이 출력된다.
//계산하고 있는 중에 이미 결과값이 나온 경우이다.
*/
System.out.println("------------------------------");
System.out.println("** main **");
CalcThread cal = new CalcThread();
cal.start();
try {
cal.join(); //thread의 모든 작업을 끝나기 전까지 '일시정지'
//join을 자기를 호출한 메소드를 멈춘다.
} catch (InterruptedException e) {
System.out.println("예외 발생");
}
System.out.println("--------------");
System.out.println("1-100까지의 합 : "+ cal.getSum()); // o이 출력된다.
//계산하고 있는 중에 이미 결과값이 나온 경우이다.
}
}
◐.동기화
임계영역 - 여러곳에서 공통적으로 쓰이는 것(여러쓰레드가 공통으로 쓰는부분)
// 요리사 - thread
public class Cook implements Runnable {
private final Dish dish; //final설정시 변경불가하다. (선언만 한 상태- 초기화는 생성자에서)
public Cook(Dish dish) {
this.dish = dish; // 초기화
}
//(int i) : 음식 종류
private void cooking(int i) throws InterruptedException { // 호출하는 쪽에서 처리하라고 던졋다.(throws)
//공유 자원을 동기화 - 작업시 못들어온다
// - 요리사와 고객이 음식을 공유 하기 때문에 동기화 시킴
synchronized(dish) { //동기화 블록
while(!dish.isEmpty()) { //빈 접시가 아니면
dish.wait(); // 기다려라
}
//빈 접시가 아니면 음식을 먹어 접시를 비우면 된다
dish.setEmpty(false);
System.out.println(i + "번재 음식이 준비되었습니다.");
dish.notify(); // wait()로 인해 일시정지 상태인 경우 실행 대기 상태로 만들기
// 손님에게 요리 다 되었다고 통보
}
}
@Override
public void run() {
for(int i = 1 ; i <= 5; i++) { //5가지 요리를 차례로 먹는다.
try {
cooking(i);
Thread.sleep(200);
} catch (InterruptedException e) {
System.out.println("예외 발생");
}
}
}
}
public class Customer implements Runnable {
private final Dish dish; //final설정시 변경불가하다. (선언만 한 상태- 초기화는 생성자에서)
public Customer(Dish dish) {
this.dish = dish; // 초기화
}
//(int i) : 음식 종류
private void eat(int i) throws InterruptedException { // 호출하는 쪽에서 처리하라고 던졋다.(throws)
//공유 자원을 동기화 - 작업시 못들어온다
// - 요리사와 고객이 음식을 공유 하기 때문에 동기화 시킴
synchronized(dish) { //동기화 블록
while(dish.isEmpty()) { //빈 접시면
dish.wait(); // 기다려라
}
//빈 접시가 아니면 음식을 먹어 접시를 비우면 된다
dish.setEmpty(true);
System.out.println(i + "번째 음식을 먹었습니다.");
dish.notify(); // wait()로 인해 일시정지 상태인 경우 실행 대기 상태로 만들기
// 요리사에게 음식을 다 먹었다고 통보
}
}
@Override
public void run() {
for(int i = 1 ; i <= 5; i++) { //5가지 요리를 차례로 먹는다.
try {
eat(i);
Thread.sleep(200);
} catch (InterruptedException e) {
System.out.println("예외 발생");
}
}
}
}
public class Dish {
private boolean empty = true;
public boolean isEmpty() { // boolean형 getter에는 is로 명명하는 것이 좋다.
return empty;
}
public void setEmpty(boolean emprty) {
this.empty = empty;
}
}
public class DishThreadMain {
public static void main(String[] args) {
final Dish d = new Dish();
new Thread(new Cook(d)).start();
new Thread(new Customer(d)).start();
}
}
◐
'1. JAVA > 3). 자바_개념' 카테고리의 다른 글
자바_개념_Day_33 (0) | 2024.02.19 |
---|---|
자바_개념_Day_32 (0) | 2024.02.15 |
자바_개념_Day_30 (0) | 2024.02.13 |
자바_개념_Day_29 (1) | 2024.02.08 |
자바_개념_Day_27 (1) | 2024.02.06 |