26. Thread (스레드) [ JAVA ]

duck-ach·2022년 8월 17일
0

JAVA

목록 보기
26/27

Thread

스레드(Thread)란?

  • 프로세스의 세부 실행 단위를 말한다.
  • 자바 실행의 기본 단위이다.
  • main스레드 이외의 추가가 가능하다.

Thread 생성

방법은 두가지가 있다.

    1. Thread 클래스를 상속받는다. (extends Thread)
    1. Runnable 인터페이스를 구현한다. (implements Runnable)

Thread 클래스 상속받는 방법

    1. Thread 클래스를 상속받게 되거나, Runnable인터페이스를 구현하게 되면, public void run() 메소드를 오버라이드 해야한다. (오버라이드 하는 이유 : 약속이기 때문)

Thread 실행

    1. start() 메소드를 호출해야한다. (중요)
    1. start() 메소드를 호출하면 run() 메소드에 오버라이드 한 내용이 실행된다.
  • Thread의 동작은 run() 안에 명시해야한다. (약속)

JAVA의 Process(프로세스)

프로세스 예제 1)

자바의 프로세스의 동작을 살펴보자.
메인 메소드에서 System.out.println()을 하면

System.out.println("main 시작");
System.out.println("main 종료"); 

출력 :

main 시작
main 종료

우리가 알던대로 이렇게 바로 출력이 된다.

프로세스 예제 3)

이제 Thread를 만들어 보자.
Thread 클래스를 상속 받을 것이다.
Process자식클래스

public class Process extends Thread {
	
	private String name;

	public Process(String name) {
		super();
		this.name = name;
	}
	
	@Override // 오버라이드 애너테이션 추가
	public void run() {
		// millies = 1/1000
		try {
			Thread.sleep(3000); // 1000 = 1초, 3초 일시중지
			System.out.println(name + " 작업 실행");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	
	
}

sleep()메소드를 사용하면 1/1000초를 기다렸다가 Thread를 실행 시킬 수 있다. 1000을 입력하면 1초가 되는 셈이다.
예외처리로는 만약 Thread끼리 부딪히거나 겹치게 되면 무한 대기상태가 생길 수 있는데 InterruptedException 를 이용하여 그것에 대한 예외처리를 지정해준다.

Main클래스

public class Main {

	public static void main(String[] args) {
		
		System.out.println("main 시작");
		
		Process process = new Process("연산");
		process.start(); // Process 클래스의 오버라이드된 run() 메소드가 호출
		
		Process process2 = new Process("제어");
		process2.start();
		
		System.out.println("main 종료"); 
		
		// Main은 시작, 메소드, 종료 순으로 일만시키고 메소드는 다른애한테 맡겼기때문에 나중에 실행된다.
		// 그래서 시작 -> 종료 -> 메소드 순으로 실행이 된다.
	}

}

출력 :

main 시작
main 종료

// wait()에 지정해준 대로 3초 뒤에 출력된다.
제어 작업 실행
연산 작업 실행

Thread 클래스 상속받기

Thread 클래스를 상속받게 되면, run()메소드를 오버라이드하고, 그 안에 Thread의 행동을 명시해야한다.

예제 )

package ex02_thread;

public class Soldier extends Thread {
	
	private String name;
	private Gun gun;
	
	public Soldier(String name, Gun gun) {
		super();
		this.name = name;
		this.gun = gun;
	}
	
	public void shoot() {
		System.out.print(name + "이(가) 총을 쐈다! ");
		gun.shoot();
	}
	
	@Override
	public void run() {
		// 1초에 한 발씩 쏘기
		
		try {
			while (gun.getBullet() != 0) {
				shoot();
				Thread.sleep(1000);
			}			
		} catch (InterruptedException e) { // 작업을 하고 있는 도중에 가로채기 당하여 대기를 하는 것
			e.printStackTrace();
		}
		
	}
	
}
package ex02_thread;

public class Gun {

	private int bullet;

	public Gun(int bullet) {
		super();
		this.bullet = bullet;
	}
	
	public void shoot() {
		if(bullet == 0) {
			System.out.println("총알 없음");
			return;
		}
		bullet--;
		System.out.println("빵야! " + bullet + "발 남음");
	}

	public int getBullet() {
		return bullet;
	}

	public void setBullet(int bullet) {
		this.bullet = bullet;
	}
	
	
	
}
package ex02_thread;

public class Main {

	public static void main(String[] args) {
		
		// 스레드 우선순위
		System.out.println("가장 높은 우선순위 : " + Thread.MAX_PRIORITY); // 10
		System.out.println("가장 낮은 우선순위 : " + Thread.MIN_PRIORITY); // 1
		System.out.println("보통 우선순위 : " + Thread.NORM_PRIORITY); // 5
		
		Soldier s1 = new Soldier("김상사", new Gun(6));
		Soldier s2 = new Soldier("장병장", new Gun(10));
		
		System.out.println("s1 우선순위 : " + s1.getPriority()); // 5
		System.out.println("s2 우선순위 : " + s2.getPriority()); // 5
		// 우선순위가 같기 때문에 쓰레드가 알아서 작업한다.
		// 우선순위가 높은 스레드를 최대한 먼저 실행한다.
		
		// 우선순위 조정
		s1.setPriority(Thread.MIN_PRIORITY);
		s2.setPriority(Thread.MAX_PRIORITY);
		
		
		// 스레드 실행
		s1.start();
		s2.start();
		
	}

}

setPriority()메소드를 이용해서 우선순위를 최대한 조정할 수 있다.
무조건 적인게 아니라서 알고 있어야 한다.

Runnable 인터페이스를 구현하여 Thread 구현하기

package ex03_runnable;

// 스레드 생성 방법
// Runnable 인터페이스 구현
// 2. public void run() 오버라이드

public class WashRobot extends Robot implements Runnable {
	
	private String name;
	
	public WashRobot(String name) {
		super();
		this.name = name;
	}

	@Override
	public void run() {
		System.out.println(name + " 빨래중");
	}
	
}
package ex03_runnable;

public class Robot {

}
package ex03_runnable;

public class Main {

	public static void main(String[] args) {

		// Runnable 인터페이스를 구현한 클래스는 Thread로 바꿔야 start()메소드로 호출할 수 있다.
		
		Runnable robot1 = new WashRobot("로봇1");
		WashRobot robot2 = new WashRobot("로봇2");

			Thread thread1 = new Thread(robot1);
			Thread thread2 = new Thread(robot2);
			Thread thread3 = new Thread(new WashRobot("로봇3"));
			
			thread1.start();
			thread2.start();
			thread3.start();
		
	}

}

join() 메소드

join() 메소드를 활용하면 모든 Thread의 동작이 종료(die)될 때까지 기다린다.

예제 1) join() 메소드를 이용하여 계산 출력

package ex04_join;

public class Calculator implements Runnable {
	long total;
	long begin;
	long end;
	
	
	
	public Calculator(long begin, long end) {
		super();
		this.begin = begin;
		this.end = end;
	}
	
	
	
	public void add() {
		for(long n = begin; n <= end; n++) {
			total += n;
		}
	}
	
	


	@Override
	public void run() {
		add();
	}



	



	public long getTotal() {
		return total;
	}



	public void setTotal(long total) {
		this.total = total;
	}



	public long getBegin() {
		return begin;
	}



	public void setBegin(long begin) {
		this.begin = begin;
	}



	public long getEnd() {
		return end;
	}



	public void setEnd(long end) {
		this.end = end;
	}
	
}
package ex04_join;

public class Main {

	public static void main(String[] args) throws InterruptedException {
	
		// Calculator를 2개 준비
		// 작업을 반으로 나눠서 진행한다.
		
		// Calculator가 동시에 연산을 수행하려면 스레드로 처리해야 한다.
		
		Calculator calc1 = new Calculator(1, 5000);
		Thread thread1 = new Thread(calc1);
		thread1.start();
		
		Calculator calc2 = new Calculator(5001, 10000);
		Thread thread2 = new Thread(calc2);
		thread2.start(); 
        

이렇게만 하면 메인메소드가 종료되는대로 결과를 출력해내기 때문에 0이라는 결과를 얻게 된다.

그래서 join() 메소드를 활용하여 Thread의 각각의 작업이 다 끝날때까지 기다렸다가 출력해 내야한다.

public class Main {

	public static void main(String[] args) throws InterruptedException {
	
		// Calculator를 2개 준비
		// 작업을 반으로 나눠서 진행한다.
		
		// Calculator가 동시에 연산을 수행하려면 스레드로 처리해야 한다.
		
		Calculator calc1 = new Calculator(1, 5000);
		Thread thread1 = new Thread(calc1);
		thread1.start();
		
		Calculator calc2 = new Calculator(5001, 10000);
		Thread thread2 = new Thread(calc2);
		thread2.start();
				
//		Thread.sleep(1000);
		
		// 모든 계산기의 동작이 끝날때까지 기다린다.
		// 스레드가 종료(die)될 때까지 기다린다.
		thread1.join();
		thread2.join();
		
		System.out.println(thread1.isAlive()); // true
		System.out.println(thread2.isAlive()); // false 살아있는가?
		
		System.out.println(calc1.getTotal() + calc2.getTotal());
		

	}

}

위의 Main클래스에서 이렇게 join()을 해주면, 정상적으로 50005000이라는 출력값을 얻게된다.

profile
자몽 허니 블랙티와 아메리카노 사이 그 어딘가

0개의 댓글