[Java] Thread

SeongWon Oh·2021년 8월 21일
0

Java

목록 보기
37/39
post-thumbnail

Thread란?

  • CPU가 동작하는 가장 작은 단위의 unit을 thread라고 한다.
  • Process는 하나 이상의 thread를 갖게되며 thread는 작업 수행의 단위이다.
  • 사용자들이 2개의 같은 program을 이용하는 경우는 program들은 same code를 실행하고 data들을 공유하고 싶을 것이다. 그래서 code와 data를 같이 공유하며 사용하자라고 만든 것이 thread이다.

Multi-threading

  • 여러 Thread가 동시에 수행되는 프로그래밍을 의미하며 여러 작업이 동시에 수행됨에 따라 효율적이다.
  • Thread 사이에서는 공유하는 자원이 있을 수 있다. 자바에서는 static instance를 공유한다.
  • 여러 thread가 자원을 공유하여 작업이 수행되는 경우 서로 자원을 차지하려는 race condition이 발생할 수 있다. 이때 공유하는 자원 중 경쟁이 발생하는 부분을 critical section이라고 한다.
  • critical section에 대한 동기화를 구현하지 않으면 오류가 발생할 수 있다.
  • 장점
    • Resource를 공유할 수 있다.
    • Process보다 생성이 가볍다.
  • 단점
    • 각각의 process가 PCB, OS resource를 가져서 효율적이지 않다.
    • process들끼리 서로 메모리를 공유하려면 IPC를 필요로 한다.

Thread를 생성하는 방법

java에서 thread를 만드는 방법은 2가지가 있다.

  • Thread class를 상속받아 만드는 방법
  • Runnable interface를 implements하는 방법

👨🏻‍💻 Example Code (Thread class를 상속받아 만드는 방법)

package ch20;

class MyThread extends Thread {
	//run은 thread가 실행되면 불리는 함수이다.
	public void run() {
		int i;
		for (i=1;i<=200;i++) {
			System.out.print(i + "\t");
		}
	}
}

public class ThreadTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		System.out.println(Thread.currentThread() + "start");
		// Thread.currentThread()의 결과인 Thread[main,5,main]은
		// 첫번째 main은 thread를 호출한 함수를 의미하며 (여기서는 main함수)
		// 두번째 5는 priority, 세번째는 해당 thread가 속해있는 위치를 의미한다.
		MyThread th1 = new MyThread();
		MyThread th2 = new MyThread();
		
		
		// thread class는 start()메소드 실행하면 thread가 실행된다.
		th1.start();
		th2.start();
		
		System.out.println(Thread.currentThread() + "end");
	}

}

👨🏻‍💻 Example Code (Runnable interface를 implements하는 방법)

package ch20;

class MyThread2 implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		int i;
		for (i=1;i<=200;i++) {
			System.out.print(i + "\t");
		}
	}
}

public class ThreadTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		
		System.out.println(Thread.currentThread() + "start");
		
		
		MyThread2 runnable = new MyThread2();
		Thread th1 = new Thread(runnable);
		Thread th2 = new Thread(runnable);
		
		th1.start();
		th2.start();
		System.out.println(Thread.currentThread() + "end");
				
	}

}
  • Runnable을 사용하여 만든 Thread방법은 thread class를 상속받은 것이 아니기에 바로 start method와 같은 Thread의 method를 사용할 수 없다. 그래서 아래와 같이 Runnable을 구현한 class를 통해 runnable 인스턴스를 생성하고 해당 인스턴스를 통해 Thread 클래스를 생성해야한다.
	MyThread2 th1 = new MyThread2();
	th1.start();Thread를 상속한 class는 위와 같이 사용하였다면 Runnable을 구현한 class는 아래와 같이 사용해야한다. 
    
    	MyThread2 runnable = new MyThread2();
	Thread th1 = new Thread(runnable);
	th1.start();
  • Runnable은 간단하게 코드를 실행할 경우 아래와 같이 익명 객체로 바로 만들 수도 있다.
	Runnable run = new Runnable() {

		@Override
		public void run() {
			// TODO Auto-generated method stub
			System.out.println("\nRun");
		}
			
	};
	run.run();

Thread의 여러 Method들

Thread 우선순위

  • Priority는 1부터 10까지 있으며 Defualt priority는 5이다.

  • 우선순위가 높다고 무조건 CPU배분을 먼저 받는것은 아니고 배분 받을 확률이 높은 것이다.
    단, Priority가 차이가 분명하게 나면 배분 순서도 확실히 정해진다.

  • setPriority()/getPriority()를 통해 priority를 주고 읽어올 수 있다.

  • Thread.MIN_PRIORITY(=1), Thread.MAX_PRIORITY(=10), Thread.NORMAL_PRIORITY(=5)를 통해 우선순위 세팅을 할 수도 있다.

👨🏻‍💻 Example Code

class PriorityThread extends Thread{	
	public void run(){
	
		int sum = 0;
		
		Thread t = Thread.currentThread();
		System.out.println( t + "start");
		
		for(int i =0; i<=1000000; i++){			
			sum += i;
		}		
		System.out.println( t.getPriority() + "end");
	}
}


public class PriorityTest {
	public static void main(String[] args) {

		int i;
		for(i=Thread.MIN_PRIORITY; i<= Thread.MAX_PRIORITY; i++){
			
			PriorityThread pt = new PriorityThread();
			pt.setPriority(i);
			pt.start();
			// Priority들의 차이가 별로 안커서 끝난 순서가 1부터 완전히 순서대로 실행되지는 않는다.
		}
	}
}

join()

  • 동시에 2개 이상의 Thread가 실행 될 때 하나의 Thread가 다른 Thread의 결과를 참조하여 실행해야 하는 경우 join()을 사용한다.

  • join()을 호출 한 Thread는 다른 Thread의 수행이 끝날때까지 non-runnable(waiting)상태로 가며 다른 Thread의 수행이 끝나면 다시 Runnable(Running)상태로 돌아가서 작업을 수행한다.

  • join을 하면 해당 thread의 작업이 끝날때까지 다른 Thread가 non-runnable상태로 작업이 끝나기를 기다려야한다. 작업이 끝나면 main을 꺠우기 위해 InterruptException을 통해 깨운다.

interrupt()

  • Interrupt는 요청한 작업이 끝났다는 것을 알리기 위해 보내는 신호이다.

  • Thread가 join(), sleep(), wait() 함수에의해 non-runnable 상태가 되었을 때 interrupt() 메서드를 호출하면 다시 runnable상태가 될 수 있다.

  • 다른 Thread에 예외를 발생시키는 interrupt를 보내서 다시 작업이 실행되게 해준다.

👨🏻‍💻 Example Code (Join, interrupt)

package ch21;

public class JoinTest extends Thread{
	
	int start;
	int end;
	int total;
	
	public JoinTest(int start, int end) {
		this.start = start;
		this.end = end;
	}
	
	
	public void run() {
		int i;
		for (i = start; i < end; i++) {
			total += i;
		}
	}
	
	
	
	public static void main(String[] args) {
		JoinTest jt1 = new JoinTest(1,50);
		JoinTest jt2 = new JoinTest(51,100);
		
		/* 이렇게 join을 하지 않는 경우 print를 하는 순간 결과가 다 안나온 상태라
		 * 실행을 할때마다 값이 달라지고 0이라는 결과가 나오기도 한다.
		jt1.start();
		jt2.start();
		
		int lastTotal = jt1.total + jt2.total;
		
		System.out.println("jt1.total = " + jt1.total);
		System.out.println("jt2.total = " + jt2.total);
		
		System.out.println("lastTotal = " + lastTotal);
		 */
		
		jt1.start();
		jt2.start();
		
		// join을 하면 해당 thread의 작업이 끝날때까지 
		// 다른 Thread(여기서는 main)이 non-runnable상태로 다음 작업을 기다려야한다.
		// 작업이 끝나면 main을 꺠우기 위해 interruptException을 통해 깨운다.
		try {
			jt1.join();
			jt2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		int lastTotal = jt1.total + jt2.total;
		
		System.out.println("jt1.total = " + jt1.total);
		System.out.println("jt2.total = " + jt2.total);
		
		System.out.println("lastTotal = " + lastTotal);
		
	}
}

Thread 종료하기

  • 과거에는 stop()이라는 메서드를 사용했는데 이제는 사용하지 않고 while(flag)의 flag 변수값을 false로 바꾸어 종료를 시킨다.

👨🏻‍💻 Example Code

import java.io.IOException;

public class TerminateThread extends Thread{

	private boolean flag = false;
	int i;
	
	public TerminateThread(String name){
		super(name);
	}
	
	public void run(){
				
		while(!flag){
			try {
				sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}		
		System.out.println( getName() + " end" );		
	}
    
	public void setFlag(boolean flag){
		this.flag = flag;
	}
		
	public static void main(String[] args) throws IOException {

		TerminateThread threadA = new TerminateThread("A");
		TerminateThread threadB = new TerminateThread("B");
		TerminateThread threadC = new TerminateThread("C");
		
		threadA.start();
		threadB.start();
		threadC.start();
		
		int in;
		while(true){
			in = System.in.read();
			if ( in == 'A'){
				threadA.setFlag(true);
			}else if(in == 'B'){
				threadB.setFlag(true);
			}else if( in == 'C'){
				threadC.setFlag(true);
			}else if( in == 'M'){
				threadA.setFlag(true);
				threadB.setFlag(true);
				threadC.setFlag(true);
				break;
			}
		}		
		System.out.println("main end");		
	}
}



Reference

profile
블로그 이전했습니다. -> https://seongwon.dev/

0개의 댓글