Joslynn의 하루

대용량 웹서비스를 위한 MSA Full-Stack 개발자 양성 과정 -18일차 노트 필기_Thread (스레드), synchronized 제한자, 동기화 블록, 람다식 표현 _220816 본문

MSA Full-Stack 개발자 양성과정/Java

대용량 웹서비스를 위한 MSA Full-Stack 개발자 양성 과정 -18일차 노트 필기_Thread (스레드), synchronized 제한자, 동기화 블록, 람다식 표현 _220816

Joslynn 2022. 8. 16. 18:20
Thread

: 하나의 프로그램 안에서 여러 개의 작업을 동시에 일하는 것처럼 만들어주기 위해서 하나의 작업의 시간을 잘게 쪼개어(얇은 실타래처럼) 여러 개의 작업을 번갈아가면서 할 수 있도록 하는 것 == 멀티스레드

: CPU 자체는 동시에 여러 개의 일을 할 수 없지만, 작업시간을 잘게 쪼개 번갈아 작업함으로써 사용자 입장에서는 동시에 작업되는 것처럼 느껴짐

: 작업시간이 오래 걸리는 걸려 다른 일을 같이 해야하는 경우에만 사용

: 특정 스레드가 일을 많이 하는 것 같으면, 스레드 풀의 다른 스레드가 들어와서 일을 하는 등의 방식 // 해당 작업 순서 및 시간을 결정하는 것은 CPU

 

: main 자체도 하나의 스레드

: 단일 스레드는 하나의 작업이 끝나기 전까지는 다음 작업을 할 수 없음

 

 

≠멀티태스킹 = 하나의 응용프로그램 안에 여러 개의 프로그램이 동시에 작업되는 것; 


스레드를 만드는 법

: Thread 객체를 만든 후 오버라이딩한 run() 메소드를 안에 기능들을 넣어줌

  **main Thread : 직접 호출하지 않음;(단일 스레드 방지)

:~.start() 스레드의 시작을 알리는 메소드

: 스레드 및 main 스레드가 Thread Pool에서 대기하고 있음

 

1) 상속 - extends

 

class A extends Thread{

    @ovverride
    public void run(){
    
    // Thread로 동작해야 할 일을 작성
    
    }
}

** 호출

A a = new A();

a.start(); // 스레드가 대기(준비) 상태가 된다.

 

**주의사항: run()을 직접 호출하면 thread로 동작을 못한다.(그냥 일반 메소드 호출하는 것; 순차적으로 )

 

2) 구현 - implements

class B implements Runnable{ // run() 메소드 하나만 존재
	@override
    public void run(){
    
    // 스레드로 동작해야 할 일
    
    }
}

class B는 Runnable 변수에 담을 수는 있지만 Thread 변수 안에 담을 수는 없음;

Runnable은 run 메소드를 재정의하기 위한 인터페이스

 

**호출

B b = new B();

Thread th = new Thread(b); // Runnable을 인수로 넣어줌;

th.start();

 

Thread 예시)

package ex0816.thread;

public class ThreadExam {

	public static void main(String[] args) {
		System.out.println("***메인 시작합니다.***");
		NumberThread th1 =  new NumberThread("첫 번째 스레드");
		NumberThread th2 =  new NumberThread("두 번째 스레드");
		
		AlphaThread alpha = new AlphaThread();
		Thread th3 = new Thread(alpha, "세 번째 스레드");
        
        // 우선순위
		th1.setPriority(Thread.MAX_PRIORITY);
		th2.setPriority(Thread.MIN_PRIORITY);
		th3.setPriority(Thread.NORM_PRIORITY);
		
        // 스레드 작업 시작 
		th1.start();
		th2.start();
		th3.start();
		
   	     try {
			th1.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// 특정한 Thread가 일을 다 마칠때까지 아래 문장을 대기(메인스레드가 대기)
		System.out.println("총 합 = " + th1.sum);
		System.out.println("***메인 끝 입니다.***");

	}

}

/**
 * 1~100까지 출력하는 기능 스레드
 * */

class NumberThread extends Thread{
	
	public NumberThread(String name){
		super(name);
	}
	
	@Override
	public void run() {
		for (int i =0; i<=100; i++) {
			System.out.println(super.getName()+ "=>"+ i);
		}
		System.out.println(super.getName()+"종료");
		
	}
}
/**A~Z까지 출력하는 기능 Thread
 * */

class AlphaThread implements Runnable{

	@Override
	public void run() {
		
		Thread th = Thread.currentThread(); // 현재 running중인 thread 객체 반환
		
		for (char ch ='A'; ch<='Z'; ch++) {
			System.out.println(th.getName()+ "=>"+ ch);
		}
		System.out.println(th.getName()+"종료");	
	}
	
}

 


Thread 객체의 주요 메소드

1) join메소드

: 현재 실행중인 메소드를 강제로 lock(실행대기) 상태로 변환하고 join 메소드 대상 스레드를 실행 및 종료될때까지 대기하게 하는 메소드

 

참고 블로그

 

[Java] 스레드 상태제어 - join

스레드를 상태제어할 수 있는 메소드 중 하나로, 현재 실행중인 메소드를 강제로 lock(실행대기) 상태로 변환하고 join 메소드 대상 스레드를 실행 및 종료될때까지 대기하게 하는 메소드이다.아

velog.io

 

 

2) sleep 메소드

: 특정 스레드가 반복적으로 일을 너무 많이 하는 것을 방지하기 위해 스레드를 잠시 중지시키는 것

: 일정시간의 중지가 끝나면 대기(준비)상태로 간다. // 바로 실행으로 들어갈 수 없음 

: 무한 루프 돌릴 때, 특히 많이 사용

: ex> 시계 // 프로그램이 꺼질 때까지 1초마다 일하면 됨, sleep 메소드를 통해 일하는 빈도 수 제어

: 해당 스레드가 잠들어있는 동안 다른 스레드가 제어권을 가져와서 일할 수 있음

: static 메소드로 Thread.로 접근 가능;

 

3) yield메소드

: 양보 (실행중 상태에서 대기상태로 이동)

: static 메소드로 Thread.로 접근 가능;

**중지, 대기상태의 차이: 대기상태는 우선순위가 높다고 생각되면 바로 실행으로 들어올 수 있음 // 이미 대기상태이므로; 

          ↔ 중지는 해당 시간이 끝날때까지 바로 실행으로 들어갈 수는 없음 (대기상태로 들어감)

 


람다식 표현

@FunctionalInterface

: 현재 interface가 한 개의 abstract 메소드를 가지고 있다는 표현

: 람다식 표현을 사용할 수 있다는 뜻 (-> 기능 구현)

: 함수적 선언 == 소스는 간결해진다.

: 람다식 표현식은 @FunctionalInterface가 선언된 interface 안에 있는 메소드가 한 개만 존재하는 경우, 구현 클래스를 만들 필요 없이 바로 기능을 구현할 수 있도록 해주는 문법

 

**원래 코딩 스타일

class Test implements Runnable{

	@override
    public void run(){
    	// 기능 작성
    }

}

 

** 람다식 표현

Runnable r = () -> { // Runnuble에 있는 인수가 없는 메소드

	//기능 작성

}

 


동기화(synchronized)

멀티스레드의 단점

: 하나의 프로그램 내의 하나의 자원을 여러 스레드가 공유하다보면, 눈에 보이지 않는 데이터 손실이 일어날 수 있음

: 해결방법 동기화 블럭을 만든다 (synchronized)

: synchronized → 특정 스레드가 동기화 블럭 영역을 다 마칠때까지 다른 스레드는 대기상태                 

: ex) 임계 영역(critical section), lock을 잡고 있다라고 표현

: 만약 특정 스레드가 임계 영역에서 error로 인해 못 빠져나온다면 다른 스레드는 무한 대기에 빠짐 → Deadlock(교착 상태) 

 

**Deadlock 해결 방법: 

wait() 메소드:  특정 Thread를 중지 상태로 만듦

notify(): wait에 의해 중지 상태인 Thread를 대기 상태로 이동 (우선 순위가 높은 스레드)

notifyAll(): wait에 의해 중지 상태인 Thread를 대기 상태로 이동 (모든 스레드)

 

동기화     : 어느 메소드가 실행하는 동안 다른 메소드를 실행이 불가능하게 블락하는 것 말한다.

비동기화  : 어느 메소드를 실행하는 도중에도 다시 메소드를 실행이 가능하다. 

 


동기화 방법

1) method 자체를 동기화

2) 원하는 작업 부분만 동기화 블럭

Comments