organize/자바

Java 33

001cloudid 2024. 8. 21. 12:12
728x90

프로세스와 쓰레드

  • 프로세스 : 실행중인 프로그램. 자원(resource)과 쓰레드로 구성
  • 쓰레드 : 프로세스 내에서 실제 작업을 수행. 모든 프로세스는 최소한 하나의 쓰레드를 가지고 있음
  • 프로세스 ≒ 공장, 쓰레드 ≒ 일꾼
  • 싱글 쓰레드 프로세스 : 자원 + 1개의 쓰레드
    멀티 쓰레드 프로세스 : 자원 + n개의 쓰레드
  • 하나의 프로세스를 생성하는 것보다 하나의 새로운 쓰레드를 생성하는 것이 더 적은 비용이 듬
    CGI(싱글 쓰레드) → 자바 서블릿(멀티 쓰레드 지원)

멀티쓰레드의 장,단점

  • 장점
    시스템 자원을 보다 효율적으로 사용할 수 있음
    사용자에 대한 응답성(responseness)이 향상됨
    작업이 분리되어 코드가 간결해짐
  • 단점 → "공유"
    동기화(synchronization)에 주의해야함
    교착상태(dead-lock)가 발생하지 않도록 주의해야함. 기아 현상 발생
    각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야함

쓰레드의 구현과 실행

구현에는 2가지 방법 Thread 클래스를 상속, Runnable 인터페이스를 구현

  • Thread클래스를 상속
class MyThread extends Thread{ //자바는 단일 상속만 가능
	public void run(){ //Thead 클래스의 run()을 오버라이딩
		/* 작업 내용 */    
    }
}

//쓰레드 생성과 실행
MyThread t = new MyThread();
t.start();
  • Runnable 인터페이스를 구현 → 더 나음(단일 상속만 가능하기 때문에)
class MyThread2 implements Runnable{
	public void run(){ //Runnable 인터페이스의 추상메소드 run()을 구현
    	/* 작업 내용 */
    }
}

//쓰레드 생성과 실행
Runnable r = new MyThread2();
Thread t = new Thread(r); //Thread(Runnable r)
//위의 두 줄 Thread t = new Thread(new MyThread2());
t.start();

 

package chapter13;

//Thread 클래스를 상속해서 쓰레드를 구현
class Thread1_1 extends Thread{
	public void run() {
		for(int i = 0; i < 500;i++) {
			System.out.print(0); //조상 Thread의 getName() 호출
		}
	}
}

//Runnable 인터페이스를 구현해서 쓰레드를 구현
class Thread1_2 implements Runnable{
	public void run() {
		for(int i = 0; i<500;i++) {
			System.out.print(1); //Thread.currentThread() 현재 실행중인 Thread를 반환
		}
	}
}

public class 쓰레드2 {
	
	public static void main(String[] args) {

		Thread1_1 t1 = new Thread1_1();
		t1.start();

		Runnable r = new Thread1_2();
		Thread t2 = new Thread(r);
		t2.start();
	}

}

실행할 때마다 다른 결과가 번갈아가면서 나타남

 

쓰레드의 실행 - start()

  • 쓰레드를 생성한 후에 start()를 호출해야 쓰레드가 작업을 시작함
  • 쓰레드를 먼저 실행했다고 먼저 실행되진 않음. 먼저 실행될 확률이 높음.
    쓰레드를 실행했을 때 실행 가능한 상태가 되는 것, 바로 실행되지 않음
    실행 순서는 OS의 스케줄러가 결정
Thread t1 = new Thread(); //쓰레드 t1을 생성
Thread t2 = new Thread(); //쓰레드 t2를 생성
t1.start(); //쓰레드 t1을 실행
t2.start(); //쓰레드 t2를 실행
//t1을 먼저 실행했기 때문에 t1이 먼저 실행될 '확률이 높음'. 무조건 t1이 실행되진 않음 → 쓰레드를 start() 했을 시 실행 가능한 상태가 되는 것이지 바로 실행되지 않음 → OS의 스케줄러가 실행 순서 결정

 

start()와 run()

class Test{
	public static void main(String args[]){
    	MyThread t1 = new MyThread(); //1.생성
        t1.start(); //2.실행
        //t2.run(); //X
    }
}

class MyThread extends Thread{
	public void run(){
    	...
    }
}
  • run을 구현하였지만 실행은 start()를 사용
    start()를 하면 새로운 호출 스택 생성
    → 새로운 호출 스택에 run()이 올라감
    → start() 종료
    → main()과 run() 서로 독립적으로 작업 수행

출처 : 자바의정석 기초편 ch13-3~6 쓰레드의 구현과 실행

main 쓰레드

  • main 메소드의 코드를 수행하는 쓰레드
  • 쓰레드는 '사용자 쓰레드'와 '데몬 쓰레드' 두 종류가 있음
    사용자 쓰레드 : main쓰레드 
    데몬 쓰레드 : 보조 쓰레드
  • 실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료됨
package chapter13;

class Thread3_1 extends Thread{
	public void run() {
		for(int i = 0; i<300;i++) {
			System.out.print(new String("-"));
		}
	}
}
class Thread3_2 extends Thread{
	public void run() {
		for(int i = 0; i<300;i++) {
			System.out.print(new String("|"));
		}
	}
}

public class 쓰레드3_1 {

	public static void main(String[] args) {
		
		Thread3_1 th1 = new Thread3_1();
		Thread3_2 th2 = new Thread3_2();
		th1.start();
		th2.start();
		long startTime = System.currentTimeMillis();
		
		try {
			th1.join(); //join() : main 쓰레드가 th1 작업이 끝날때까지 기다림
			th2.join();  
		} catch (InterruptedException e) {
		}
		
		System.out.println("소요시간 : " + (System.currentTimeMillis()-startTime));

	}

}

 

싱글쓰레드와 멀티쓰레드

  • 싱글쓰레드
    작업 A가 끝난 다음에 작업 B가 끝남
package chapter13;

public class 쓰레드3_2 {
	
	public static void main(String[] args) {
		//싱글쓰레드
		long startTime = System.currentTimeMillis();
		
		for(int i =0; i<300;i++) {
			System.out.print("-");
		}
		
		System.out.println("A작업 소요시간 : " + Math.abs(startTime-System.currentTimeMillis()));
		
		for(int i =0; i<300;i++) {
			System.out.print("|");
		}
		System.out.println("B 작업 소요시간 : " + Math.abs(startTime-System.currentTimeMillis()));

	}

}
  • 멀티쓰레드
package chapter13;

class MyThread3a extends Thread{
	public void run() {
		for(int i =0; i<300;i++) {
			System.out.print("-");
		}
	}
}
class MyThread3b extends Thread{
	public void run() {
		for(int i =0; i<300;i++) {
			System.out.print("|");
		}
	}
}

public class 쓰레드3_2 {
	
	public static void main(String[] args) {

		//멀티쓰레드
		MyThread3a th1 = new MyThread3a();
		MyThread3b th2 = new MyThread3b();
		th1.start();
		th2.start();
		
		
	}

}

 

쓰레드의 I/O 블락킹

  • 싱글쓰레드 사용자로부터 입력을 기다리는 구간 아무 작업도 하지 않음
  • 멀티 쓰레드 사용자로부터 입력을 기다리는 구간 다른 작업을 수행함
package chapter13;

import javax.swing.JOptionPane;

public class 쓰레드3_3 {

	public static void main(String[] args) {
		
		//싱글
		String input = JOptionPane.showInputDialog("아무값이나 입력하세요.");
		System.out.println("입력하신 값 " + input);
		for(int i = 10; i>0 ; i--) {
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
			}
		}

		//멀티
		Thread3_3a th1 = new Thread3_3a();
		th1.start();
		String input1 = JOptionPane.showInputDialog("아무값이나 입력하세요.");
		System.out.println("입력한 값은 " + input1);
		
	}

class Thread3_3a extends Thread{
	public void run() {
		for(int i = 0;i>0;i--) {
			System.out.println(i);
			try {
				sleep(1000);
			} catch (Exception e) {
			}
		}
	}
}
	
}

※import문에서 에러가 발생할 때 src 안에 module-info.java 파일이 생성되어있는지 확인 후 삭제

 

쓰레드의 우선순위(priority of thread)

  • 작업의 중요도에 따라 쓰레드의 우선순위를 다르게 하여 특정 쓰레드가 더 많은 작업시간을 갖게 할 수 있음
void setPriority(int newPriority) //쓰레드의 우선순위를 지정한 값으로 변경
int getPriority() // 쓰레드의 우선순위를 반환
public static final int MAX_PRIORITY = 10; //최대 우선순위
public static final int MIN_PRIORITY = 1; //최소 우선순위
public static final int NORM_PRIORITY = 5; //보통 우선순위(기본적)
  • 우선 순위를 지정해준다고 반드시 우선 순위대로 작동되는 것은 아님. OS 스케줄러에 따라서 작업이 진행됨..
    즉 우선순위를 지정하는 것은 희망사항일뿐 결과는 우선 순위대로 작동되지 않음을 인지하자
    불확실성 ↑
package chapter13;

class ThreadEx4_1a extends Thread{
	public void run() {
		for(int i = 0; i<300;i++) {
			System.out.print("-");
			for(int x = 0; x<10000000;x++); //시간지연용 for문
		}
	}
}
class ThreadEx4_1b extends Thread{
	public void run() {
		for(int i = 0; i<300;i++) {
			System.out.print("|");
			for(int x = 0; x<10000000;x++);
		}
	}
}


public class 쓰레드4_1 {

	public static void main(String[] args) {
		ThreadEx4_1a th1 = new ThreadEx4_1a();
		ThreadEx4_1b th2 = new ThreadEx4_1b();
		
		th2.setPriority(7);
		System.out.println("th1의 우선순위(-) " + th1.getPriority());
		System.out.println("th2의 우선순위(|) " + th2.getPriority());

		th1.start();
		th2.start();
	}

}

 

쓰레드 그룹

  • 서로 관련된 쓰레드를 그룹으로 묶어서 다루기 위한 것
  • 모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야함
  • 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 'main쓰레드 그룹'에 속함
  • 자신을 생성한 쓰레드(부모 쓰레드)의 그룹과 우선순위를 상속
//생성자
Thread(ThreadGroup group, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)

ThreadGroup getThreadGroup() //쓰레드 자신이 속한 쓰레드 그룹을 반환
void uncaughtException(Thread t, Throwable e) //처리되지 않은 예외에 의해 쓰레드 그룹의 쓰레드가 실행이 종료되었을 때, JVM에 의해 이 메소드가 자동적으로 호출
  • 쓰레드 그룹의 메소드
ThreadGroup(String name) 지정된 이름의 새로운 쓰레드 그룹을 생성
ThreadGroup(ThreadGroup parent, String name) 지정된 쓰레드 그룹에 포함되어 새로운 쓰레드 그룹을 생성
int acftiveCount() 쓰레드 그룹에 포함된 활성상태에 있는 쓰레드의 수를 반환
int activeGroupCount() 쓰레드 그룹에 포함된 활성상태에 있는 쓰레드 그룹의 수를 반환
void checkAccess() 현재 실행중인 쓰레드가 쓰레드 그룹을 변경할 권한이 있는지 체크
void destroy() 쓰레드 그룹과 하위 쓰레드 그룹까지 모두 삭제. 단, 비어있어야 삭제 가능
int enumerate(Thread[] list)
int enumerate(Thread[] list, boolean recurse)
int enumerate(ThreadGroup[] list)
int enumerate(ThreadGroup[] list, boolean recurse)
쓰레드 그룹에 속한 쓰레드 또는 하위 쓰레드 그룹의 목록을 지정된 배열에 담고 그 개수를 반환.
두번째 매개변수인 recurse의 값을 true로 하면 쓰레드 그룹에 속한 하위쓰레드 그룹에 쓰레드 또는 쓰레드 그룹까지 배열에 담음
int getMaxPriority() 쓰레드 그룹의 최대우선순위를 반환
String getName() 쓰레드 그룹의 이름 반환
... ...

 

데몬 쓰레드(daemon thread)

  • 일반 쓰레드(non-daemon thread)의 작업을 돋는 보조적인 역할을 수행
  • 일반 쓰레드가 모두 종료되면 자동적으로 종료됨
  • 가비지 컬렉터, 자동저장, 화면 자동갱신 등에 사용됨
  • 무한루프와 조건문을 이용해서 실행 후 대기하다가 특정조건이 만족되면 작업을 수행하고 다시 대기하도록 작성함
public void run() {
	while(true) {
		try {
			Thread.sleep(3*1000); //3초마다
		} catch(InterruptedException e) {}
		
		if(autoSave) {
			autoSave();
		}
	}
}
boolean isDaemon() //쓰레드가 데몬 쓰레드인지 확인. 데몬 쓰레드이면 true를 반환
void setDaemon(boolean on) //쓰레드를 데몬 쓰레드로 또는 사용자 쓰레드로 변경. 매개변수 on을 true로 지정하면 데몬쓰레드가 됨
//※setDaemon(boolean on)은 반드시 start()를 호출하기 전에 실행되어야 함. 그렇지 않으면 IllegalThreadStateException이 발생
package chapter13;

public class 쓰레드5_1 implements Runnable{
	static boolean autoSave = false;
	
	public static void main(String[] args) {
		
		Thread t = new Thread(new 쓰레드5_1()); //Runnnable 인터페이스 구현
		t.setDaemon(true); //이 부분이 없을 시 종료가 되지 않음
		t.start();
		
		for(int i =1; i<=10; i++) {
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
			}
			System.out.println(i);
			if(i==5) {
				autoSave = true;
			}
		}
		System.out.println("프로그램을 종료합니다.");

	}

	@Override
	public void run() {
		while(true) {
			try {
				Thread.sleep(3000); //3초마다
			} catch (InterruptedException e) {
			}
			if(autoSave) {
				autoSave();
			}
		}
		
	}
	
	public void autoSave() {
		System.out.println("작업 파일이 자동 저장되었습니다.");
	}
	
}

 

쓰레드의 상태

상태 설명
NEW 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태
RUNNABLE 실행 중 또는 실행 가능한 상태
BLOCKED 등기화블럭에 의해서 일시정지된 상태(lock이 풀릴 때까지 기다리는 상태)
WAITING,
TIMED_WAITTING
쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은(unrunnable) 일시정지 상태.
TIMED_WAITTING은 일시정지시간이 지정된 경우를 의미
TERMINATED 쓰레드의 작업이 종료된 상태

생성실행대기실행소멸

                             일시정지

 

쓰레드의 실행제어

  • 쓰레드의 실행을 제어할 수 있는 메소드가 제공. 이들을 활용해서 보다 효율적인 프로그램을 작성할 수 있음
메소드 설명
static void sleep(long millis)
static void sleep(long millis, int nanos)
지정된 시간(천분의 일초 단위)동안 쓰레드를 일시정지(잠들게)시킴.
지정된 시간이 지나고 나면, 자동적으로 다시 실행대기 상태가 됨.
void join()
void join(long millis)
void join(long millis, int nanos)
지정된 시간동안 쓰레드가 실행되도록 함. 지정된 시간이 지나거나 작업이 종료되면
join()을 호출한 쓰레드로 다시 돌아와 실행을 계속함
void interrupt() sleep()이나 join()에 의해 일시정지상태인 쓰레드를 깨워서 실행대기상태로 만듦.
해당 쓰레드에서는 Interrupted Exception이 발생함으로써 일시정지 상태를 벗어나게 됨
void stop() 쓰레드를 즉시 종료시킴
void suspend() 쓰레드를 일시정지시킴. resume()을 호출하면 다시 실행대기상태가 됨
void resume() suspend()에 의해 일시정지상태에 있는 쓰레드를 실행대기상태로 만듦
static void yield() 실행 중에 자신에게 주어진 실행시간을 다른 쓰레드에게 양보(yield)하고 자신은 실행대기상태가 됨

※static 쓰레드 자기자신에게만 호출 가능

 

728x90

'organize > 자바' 카테고리의 다른 글

Java 35  (0) 2024.08.22
Java 34  (0) 2024.08.22
Java 32  (0) 2024.08.20
Java 31  (0) 2024.08.15
Java 30  (0) 2024.08.14