13.4 제한된 타입 파라미터

→ 주로 제네릭 메소드에서 쓰인다.

<T>는 기본적으로 Object로 간주.

 

경우에 따라 타입 파라미터를 대체하는 구체적인 타입을 제한할 필요가 있음.

→ 모든 타입으로 대체할 수 없고, 특정 타입과 자식 또는 구현관계에 있는 타입만 대체할 수 있는 타입 파라미터

public <T extends 상위타입> 리턴타입 메소드(매개변수, ... ) { ... }

상위타입 → 클래스, 인터페이스 가능

 

예시) 코드로 이해

public static <T extends Number> boolean compare(T t1, T t2) {
	// T의 타입 출력하기
    System.out.println("compare(" + t1.getClass().getSimpleName() + ", " + 
    					t2.getClass().getSimpleName() + ")");
                        
    // Number의 메소드 사용
    double v1 = t1.doubleValue();
    double v2 = t2.doubleValue();
    
    return (v1 == v2);
}

// main 메소드 안
boolean result1 = compare(10, 20);  // 매개값의 타입이 int 따라서 Integer로 자동 boxing되어 대입된다

 

 

13.5 와일드카드 타입 파라미터

와일드카드 = "?"로 표현

"?"는 범위에 있는 모든 타입으로 대체할 수 있다는 표시.

 

▶ 리턴타입 메소드명(제네릭타입<? extends Student> 변수) { ... }

→ Student를 포함한 자식 객체 모두 가능

 

▶ 리턴타입 메소드명(제네릭타입<? super Worker> 변수) { ... }

→ Worker를 포함한 부모 객체 모두 가능

 

▶ 리턴타입 메소드명(제네릭타입<?> 변수) { ... }

→ 모든 타입의 객체 가능

 

 


 

14장 멀티 스레드

→ 웹 서버는 스레드 처리를 함.

→ 여러 사용자가 하나의 사이트에 접속할 때를 생각해보자.

→ CS 지식 포함..

 

14.1 멀티 스레드 개념

운영체제는 실행 중인 프로그램을 '프로세스'로 관리.

● 멀티 태스킹 : 두 가지 이상의 작업을 동시에 처리하는 것.

운영체제는 멀티 프로세스를 생성하여 작업을 처리한다.

하지만, 멀티 태스킹이 꼭 멀티 프로세스를 뜻하지는 않는다.

 

하나의 프로세스 내에서 '멀티 태스킹'을 할 수 있도록 만들어진 프로그램들도 있다.

예) 메신저

 

하나의 프로세스가 두 가지 이상의 작업을 처리할 수 있는 이유

→ 멀티 스레드가 있기 때문

 

● 스레드(thread) : 코드의 실행 흐름

멀티 프로세스프로그램 단위의 멀티 태스킹이라면,

멀티 스레드프로그램 내부에서의 멀티 태스킹이라고 볼 수 있음.

멀티 프로세스들은 서로 독립적이므로 하나의 프로세스에서 오류가 발생해도 다른 프로세스에게 영향을 미치지 X.

 

반면, 멀티 스레드프로세스 내부에서 생성되기 때문에 하나의 스레드가 예외를 발생시키면 프로세스가 종료되므로 다른 스레드에게 영향을 미친다.

따라서 멀티 스레드를 사용할 경우 예외 처리에 만전을 기해야 함.

 

cf) 멀티 스레드

→ 데이터를 분할하여 병렬로 처리하는 곳에서 사용

→ 안드로이드 앱에서 네트워크 통신을 하기 위해 사용

→ 다수의 클라이언트 요청을 처리하는 서버를 개발할 때 사용

 

※ 프로그램 개발에 있어서 멀티 스레드는 꼭 필요한 기능. 반드시 이해하고 넘어가자.

 

 

14.2 메인 스레드

모든 자바 프로그램은 main 스레드가 main() 메소드를 실행하면서 시작됨.

필요에 따라 추가 작업 스레드들을 만들어서 실행시킬 수 있다.

싱글 스레드에서 메인 스레드가 종료되면 프로세스도 종료.

멀티 스레드에서는 실행 중인 스레드가 하나라도 있으면 프로세스는 종료되지 않음.

 

 

14.3 작업 스레드 생성과 실행

→ 예제 이해를 위해 9.7 익명 객체 살펴봄

자바 프로그램은 메인 스레드가 반드시 존재함.

메인 작업 이외에 추가적인 작업 수만큼 스레드를 생성하면 된다.

작업 스레드도 객체로 관리하므로 클래스가 필요.

 

Thread 클래스로 직접 객체를 생성해도 되지만, 하위 클래스를 만들어 생성할 수 있다.(예시 코드로 활용)

 

● Thread 클래스로 직접 생성 (방법 1)

java.lang 패키지에 있는 Thread 클래스로부터 작업 스레드 객체를 '직접' 생성

→ Runnable 구현 객체를 매개값으로 갖는 생성자를 호출

Thread thread = new Thread(Runnable target);

Runnable은 스레드가 작업을 실행할 때 사용하는 인터페이스. run() 메소드가 정의되어 있다.

따라서 구현 클래스는 run()을 재정의하여 스레드가 실행할 코드를 갖고 있어야 한다.

class Task implements Runnable {
    @Override
    public void run() {
        // 스레드가 실행할 코드
    }
}

Runnable 구현 클래스는 작업 내용을 정리한 것이므로 스레드에게 전달해야 한다.

Runnable task = new Task();
Thread thread = new Thread(task);  // 전달

 

구현 클래스를 따로 작성하지 않고 Thread 생성자를 호출할 때 Runnable 익명 구현 객체(9장 7절)를 매개값으로 사용 가능하다.

Thread thread = new Thread( new Runnable() {
    @Override
    public void run() {
	    // 스레드가 실행할 코드
    }
} );

 

작업 스레드 객체가 생성되었다고 해서 바로 실행되지는 않는다.

start() 메소드를 호출해주어야 한다.

 

예시)

public static void main(String[] args) {
    
    // 작업 1 ------------------------------------
    Thread thread = new Thread(new Runnable() {  // 익명 객체 생성 방법으로 진행
        @Override  // run 메소드 재정의
        public void run() {
            for(int i=0; i<5; i++) {
                Thread curThread = Thread.currentThread();  // 스레드 객체의 참조를 얻음
                curThread.setName("작업 1");  // 스레드 객체의 이름을 정함
                // 스레드가 작업할 내용
                System.out.println("익명 객체 방법 " + curThread.getName());  // 스레드 객체의 이름을 출력

                // 1초간 일시 정지
                try {
                    Thread.sleep(1000);  // 메인 스레드 1초간 정지
                } catch (InterruptedException e) {
                }
            }
        }
    }); // 작업 1의 스레드 선언과 run() 재정의 끝
    thread.start();  // 작업 1의 스레드 사용
    // ----------------------------------------

	//메인 스레드 (main문이 실행되자마자 실행되는 스레드)
    // 작업 2 ------------------------------------- // 
    for(int i=0; i<5; i++) {
        Thread curThread = Thread.currentThread();
        curThread.setName("메인");
        // 스레드가 작업할 내용
        System.out.println("가사를 출력합니다." + curThread.getName());
        // 1초간 일시 정지
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
    }
    // ----------------------------------------
}

 

 

● Thread 자식 클래스로 생성 (방법 2)

 

예시)

public class WorkerThread extends Thread {  // 자식 클래스
	@Override
	public void run() {
		for(int i=0; i<5; i++) {
			// 스레드가 작업할 내용
			System.out.println("두 번째 방법");
			// 1초간 일시 정지
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
		}
	}
}

public static void main(String[] args) {
    // 작업 1 ------------------------------------
    Thread thread = new WorkerThread(); // 스레드의 자식 객체 생성
    thread.start();
    // ----------------------------------------

    // 작업 2 -------------------------------------
    for(int i=0; i<5; i++) {
        // 스레드가 작업할 내용
        System.out.println("가사를 출력합니다.");
        // 1초간 일시 정지
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
    }
    // ----------------------------------------
}

 

예시) 익명 자식 객체 이용한 방법

public static void main(String[] args) {
    // 작업 1 ------------------------------------
    Thread thread = new Thread() {
        @Override
        public void run() {
            for(int i=0; i<5; i++) {
                // 스레드가 작업할 내용
                System.out.println("두 번째 방법");
                // 1초간 일시 정지
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    };
    thread.start();
    // ----------------------------------------

    // 작업 2 -------------------------------------
    for(int i=0; i<5; i++) {
        // 스레드가 작업할 내용
        System.out.println("가사를 출력합니다.");
        // 1초간 일시 정지
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
    }
    // ----------------------------------------
}

 

 

14.4 스레드 이름

스레드는 모두 자신의 이름을 갖고 있다.

메인 스레드는 'main'

작업 스레드는 자동적으로 'Thread - n'

작업 스레드 이름은 디버깅할 때 어떤 스레드가 작업을 하는지 확인할 목적으로 사용한다.

// 스레드 객체를 생성하고 난 후
Thread thread = Thread.currentThread();  // 스레드 객체의 참조를 얻음
thread.setName("스레드 이름");  // 스레드 이름 설정
System.out.println(thread.getName());  // 스레드 이름 출력

 

 

14.5 스레드 상태

보통 프로그램에서의 '상태'는 data를 의미한다.

여기에선 스레드가 실행되고 있느냐, 아니냐를 기준

스레드 객체 생성 후 start() 메소드를 호출하면 실행 대기 상태가 됨.

CPU 스케쥴링(운영체제)에 따라 CPU를 점유하고 run() 메소드를 실행. 이때 실행 상태가 됨.

run() 메소드를 모두 실행하기 전에 스케쥴링에 따라 다시 실행 대기 상태로 돌아갈 수 있음.

실행, 실행 대기 상태를 번갈아 자신의 run() 메소드를 조금씩 실행.

run() 메소드가 종료되면 스레드의 실행은 멈춘다.

 

구분 메소드 설명
일시 정지로 보냄 sleep(long millis) 주어진 시간 동안 스레드를 일시 정지 상태로 만듦.
주어진 시간이 지나면 자동적으로 실행 대기 상태가 됨.
join() join() 메소드를 호출한 스레드는 일시 정지 상태가 됨.
실행 대기 상태가 되려면, join() 메소드를 가진 스레드가 종료되어야 함.
wait() 동기화 블록 내에 스레드를 일시 정지 상태로 만듦.
일시 정지에서 벗어남 interrupt() 일시 정지 상태일 경우, InterruptedException을 발생시켜 실행 대기 상태 or 종료 상태로 만듦.
notify()
notifyAll()
wait() 메소드로 인해 일시 정지 상태인 스레드를 실행 대기 상태로 만듦.
실행 대기로 보냄 yield() 실행 상태에서 다른 스레드에게 실행을 양보하고 실행 대기 상태가 됨.

→ 각각의 예제를 보고 코드의 흐름 이해

'JAVA' 카테고리의 다른 글

5일차 2024 - 3 - 4  (0) 2024.03.18
4일차 2024 - 2 - 29  (0) 2024.03.18
11일차 2024 - 3 - 12  (0) 2024.03.17
10일차 2024 - 3 - 11  (0) 2024.03.17
14일차 2024 - 3 - 15  (0) 2024.03.14

+ Recent posts