본문 바로가기

코딩/JAVA

JAVA 28. 쓰레드

쓰레드 Thread

쓰레드는 독립적인 실행단위이다. 우리가 한글문서를 작성하면서 프린트로 인쇄하거나 인터넷을 하며 음악을 듣는것 처럼 한번에 두 가지 이상의 프로세스를 실행가능하게 해 준다. 하지만 실제로 동시에 두 개가 실행되지는 않고 프로세스를 쪼개서 각각 Time Slot에 넣고 번갈아 실행하므로 실은 동시에 돌아가는것처럼 보일뿐이다.

 

package java11;
//싱글 스레드
class ThreadEx extends Thread { //내장클래스 상속
	@Override
	public void run() {
		for (int i=0; i<=10; i++) {
			System.out.println("Thread activated");
		}
	}
}

public class Test07 {

	public static void main(String[] args) {
		ThreadEx t = new ThreadEx();
		t.run(); // t.start()를 사용해서 병렬스레드를 실행시킬 수 있다. start는 내장
		System.out.println("End of main method");
	}

}

 

package java11;

class Thread_mul1 extends Thread {
	@Override
	public void run() {
		for (int i=0; i<=30; i++) {
			System.out.print("100");
		}
	}
}

class Thread_mul2 extends Thread {
	@Override
	public void run() {
		for (int i=0; i<=30; i++) {
			System.out.print("200");
		}
	}
}

public class Test08 {
	public static void main(String[] args) {
		Thread_mul1 mt1 = new Thread_mul1();
		Thread_mul2 mt2 = new Thread_mul2();
		mt1.start();
		mt2.start();
	}

}

package java11;

class Thread_mul1 extends Thread {
	@Override
	public void run() {
		for (int i=0; i<=30; i++) {
			System.out.print("1");
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class Thread_mul2 extends Thread {
	@Override
	public void run() {
		for (int i=0; i<=30; i++) {
			System.out.print("2");
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class Test08 {
	public static void main(String[] args) {
		Thread_mul1 mt1 = new Thread_mul1();
		Thread_mul2 mt2 = new Thread_mul2();
		mt1.start();
		mt2.start();
	}

}

package java11;
class Thread_Ex extends Thread {
	private int[] temp;
	public Thread_Ex() {
		temp = new int[10];		//Thread_Ex temp = new Thread_Ex();
		for (int i=0; i<temp.length; i++) {
			temp[i] = i;
		}
	}
	public void run() {
		for (int i : temp) { // 확장 for문 : temp에 데이터가 있는동안
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("temp : " +temp[i]);
		}
	}
}

public class Test09 {

	public static void main(String[] args) {
		Thread_Ex te1 = new Thread_Ex();
		te1.start();  //run()을 찾아가서 실행
		try {
			Thread.sleep(2000);
			System.out.println("프로그램 종료");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

Thread클래스 (java.lang.Thread)에는 많은 메서드가 있는데 그 중 단일 쓰레드는 run()을 사용하고, 멀티 쓰레드 start()를 오버라이드해서 사용할 수 있는데, start()를 사용하면 내부적으로 run()도 실행된다. 멀티 쓰레드를 처리할 때에는 시차를 주기 위해서 Object.sleep(sec); 을 주어서 두 쓰레드 사이에 격차를 주고 실행시키기도 한다.

=> Thread와 동일한 개념으로 사용될 수 있는 것으로 Runnable 인터페이스가 있다.

class Thread_Ex extends Thread { .. } 를

class Runnable_Ex Implements Runnable { ...} 식으로 사용한다.

Runnable 인터페이스에서는 단일쓰레드만 빠르게 취급하므로 run() 하나만 사용하면 된다. start()를 쓴다면 객체를 생성해야한다.

재사용성이 높고(일단 Thread 클래스는 한번 상속받으면 타 클래스를 상속받기 어렵기 때문에 다수의 상속이 가능한 인터페이스가 좋을 수 있다.) 코드의 일관성을 유지할 수 있어서 일부 수행에 있어서는 Thread보다 더 효율적일 수 있다. start()를 사용하면 실행에 필요한 스택stack(메모리)를 확보한 뒤 run()을 각각 호출해서 사용하기 떄문에 멀티 쓰레드가 가능한 것이다.

package java11;
class Thread_ex2 implements Runnable { //class Thread_ex2 extends Thread
	int temp[];
	public Thread_ex2() {
		temp = new int[10];
		for (int i=0; i<temp.length; i++) {
			temp[i] = i;
		}
	}
	public void run() {
		for (int i=0; i<temp.length; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("temp : " + temp[i]);
		}
	}
}


public class Test10 {

	public static void main(String[] args) {
		Thread_ex2 te1 = new Thread_ex2();
		Thread te2 = new Thread(te1); //내장클래스
		te2.start();
	}

}
 
 

package java11;
public class Test12 implements Runnable  {
	public static void main(String[] args) {
		System.out.println("메인클래스 start");
		Test12 tt1 = new Test12();			
		Thread tt2 = new Thread(tt1);		//Thread의 객체를 넣고 새 Thread객체 생성
		tt2.start();							//run을 호출
		System.out.println("해피엔딩");

	}
	public void run() {
		System.out.println("run()");
		first();
	}
	public void first() {
		System.out.println("first()");
		second();
	}
	public void second() {
		System.out.println("second()");
	}
}

결과를 보면 ‘메인 클래스 시작’ > run() > first() > second() > ‘메인 클래스 종료’ 순서 대로 나와야 할 것 같지만 실제로는 그렇지 않다. ‘메인 클래스 시작’ 한 뒤 start()를 호출하기 때문에 run()로 실행되고, first(), second(), 다시 메인 클래스로 와서 ‘메인 클래스 끝’을 보여야 하지만 ‘메인 클래스 시작’ > ‘메인 클래스 종료’ > run() > first() > second() 이렇게 출력이 된다. 메인 클래스가 실행된 이후에 스레드 객체를 호출하고 start()를 사용해 run()을 호출했지만 run()이 호출되기 전에 그 아래 줄인 '메인클래스 종료'가 먼저 출력되었기 때문이다.

===>이처럼 스레드를 사용하면 '위에서 아래로, 좌에서 우'로 라고 하는 작업순서를 따르게 되지 않을 수도 있다.

 

 

package java12;

import java.util.Scanner;

public class Q1 implements Runnable {
	private int num;
	
	public Q1(int n) {
		this.num =n;
	}
	
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		System.out.print("1이상의 숫자를 입력해주세요 : ");
		
		Q1 q1 = new Q1(scan.nextInt());
		Thread th = new Thread(q1);
//		Runnable th = new Q1();

		th.start();
	}
	@Override
	public void run() {
		for (int i=num; i>=0; i--) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {}
			System.out.println(i);
		}
	System.out.println("종료");
	}
}

 

 

 

Daemon Thread

다른 일반 쓰레드의 작업을 보조하는 역할을 수행한다. 함께 구동중인 일반스레드가 종료되면 데몬스레드도 함꼐 종료된다. 예를들어 묹서를 작성하는 도중 3초간격으로 자동 세이브가 필요한 경우 사용할 수 있다.

package java12;

public class Test01 implements Runnable {
	static boolean autoSave = false;	//추상메서드 필요
	public static void main(String[] args) {
		Test01 dm = new Test01();
		Thread th = new Thread(dm);
		th.setDaemon(true);		//추상메서드
		th.start();					//추상메서드
		for (int i = 1; i<= 15; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.print("*");
			if (i==3) {
			autoSave=true;
			}
		}
		System.out.println("프로그램 종료");
	}
	public void run() {
		while (true) {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (autoSave == true) {
				System.out.println("Auto saving...");
			}
		}
	}
}

쓰레드의 Join

하나의 쓰레드가 끝난뒤 다시 이어서 작업하는것이 Join(메서드)이다.

 

package java12;

public class Test02 implements Runnable {
	public static void main(String[] args) {
		System.out.println("메인 클래스 시작");
		Test02  tj = new Test02();
		Thread th = new Thread(tj);
//		Runnable th = new Test02();
		th.start(); 				//추상메서드
		try {
			th.join();			//추상메서드, 모든 메서드가 끝날때까지 대기
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("메인 클래스 종료");
	}
	@Override
	public void run() {
		System.out.println("run()");
		first();
	}
	public void first() {
		System.out.println("first()");
		second();
	}
	public void second() {
		System.out.println("second()");
	}
}

 

'코딩 > JAVA' 카테고리의 다른 글

JAVA 29. 컬렉션 프레임워크  (0) 2022.08.08
JAVA 28-1. 쓰레드2  (0) 2022.08.08
JAVA 27. 예외  (0) 2022.08.08
JAVA 26. 내부 클래스  (0) 2022.07.31
JAVA 25. ENUM  (0) 2022.07.31