본문 바로가기
JAVA

[JAVA] Thread

by KhyeonS 2022. 8. 1.

쓰레드(Thread)에 대해 말아보자.

 


쓰레드는 독립적인 프로세스 실행단위이다. 쓰레드는 우리가 한글 문서를 작성 하면서 프린트로 인쇄를 동시에 할 수 있는 것이나 인터넷을 하면서 음악을 듣는 것처럼 한 번에 두가지 이상의 프로세스(운영체제에서 실행중인 하나의 프로그램)을 실행 가능하게 해 준다. 하지만 실제로 동시에 두개가 실행되는 것은 아니고 운영체제 내부에서 CPU의 프로세스를 쪼개서 각각을 Time Slot에 넣고 번갈아 실행하는 것이다. 동시에 돌아가는 것처럼 보이도록 아주빠르게 번갈아서 스레드가 실행되는 것인데 사람은 이를 느끼지 못할 뿐이다.

 

쓰레드에는 싱글 쓰레드와 멀티 쓰레드가 있다.

 

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()를 사용한다면 객체를 생성해서 사용하면 된다.

 

Runnable 인터페이스는 재사용성이 높고(일단 Thread 클래스를 상속받으면 다른 클래스를 상속받기 어렵기 때문에 여러 클래스를 쉽게 상속받을 수 있는 인터페이스가 좋을 수 있다.), 코드의 일관성을 유지할 수 잇어서 일부 수행에서는 Thread 보다 더 효율적일 수 있다. run()을 호출하는 것은 생성된 쓰레드를 실행하는 것이 아니라 클래스의 메써드를 실행하는 것이지만, start()를 사용하면 실행에 필요한 스택(stack:메모리)를 확보한 뒤 run()을 각각 호출해서 사용하기 때문에 멀티 쓰레드가 가능한 것이다.

 

 

 

Thread 클래스

 

- JDK에서 지원하는 java.lang.Thread 제공

 

<Thread 생성자>

 Thread()  
 Thread(String s)  스레드 이름
 Thread(Runnable r)  인터페이스 객체 
 Thread(Runnable r, String s)  인터페이스 객체와 스레드 이름 

 

<Thread 메소드>

 static void sleep(long msec) 
 throws Interrupted Exception
  msec에 지정된 밀리초 동안 대기
 String getName()  스레드의 이름을 s로 설정 
 void setName(String s)  스레드의 이름을 s로 설정
 void start()  스레드를 시작 run() 메소드 호출
 int getPriority()  스레드의 우선 순위를 반환 
 void setpriority(int p)  스레드의 우선순위를 p값으로 
 boolean isAlive()  스레드가 시작되었고 아직 끝나지 않았으면 true 끝났으면 false 반환 
 void join() throws InterruptedException  스레드가 끝날 때 까지 대기  
 void run()  스레드가 실행할 부분 기술 (오버라이딩 사용)
 void suspend()  스레드가 일시정지 resume()에 의해 다시시작 할 수 있다. 
 void resume()  일시 정지된 스레드를 다시 시작. 
 void yield()  다른 스레드에게 실행 상태를 양보하고 자신은 준비 상태로 

Single Thread

package java11;
//single Thread
class ThreadEx extends Thread{  // nested Thread class inherited
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("Thread activation");
			
		}
	}
}
public class Test07 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadEx t = new ThreadEx(); //ThreadEx() 클래스의 생성자(메써드)를 사용해서 객체 t 생성
		t.run(); // t.start()를 사용해서 병렬 쓰레드를 실행시킬 수 있다.
		System.out.println("End of Main class");
	}

}

 


 

package java11;
class Thread_mul1 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i <=50; i++) {
			System.out.print("1");
//			System.out.println("----");
		}
	}
}
class Thread_mul2 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i <=50; i++) {
			System.out.print("2");
			
		}
	}
}


public class Test08 {
	public static void main(String[] args) {
		Thread_mul1 mt1 = new Thread_mul1();
		Thread_mul2 mt2 = new Thread_mul2();
		mt1.start(); //object.start() will start multi-threads
		mt2.start(); //object.run() will start multi-threads
		
	}

}


package java11;
class Thread_Ex extends Thread{
	private int[] temp;
	public Thread_Ex() { // 생성자 메써드
		temp = new int[10]; 
		//생성자 안에서 객체를 생성할 때 이 클래스 밖에서 이를 이용한 생성자 생성은 Thread_Ex  temp = new Thread_Ex(), 
		//temp.run/start() 식으로 처리했어야 한다.
		for (int i = 0; i < temp.length; i++) {
			temp[i] = i;
		}
	}
	public void run() {
		for(int i :temp) { // 확장 for문 : temp에 데이터가 있는 동안
		try {
			Thread.sleep(1000);// 1초 대기
		} 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.run();  
		// 멀티 처리를 위한 start ()를 호출하면 단일 처리를 해주는 run()도 내부적으로 실행된다. 
		try {
			Thread.sleep(2000);// 2초 대기
			System.out.println("프로그램 종료");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}  
	}
	

}

 

package java11;
 class Thread_Ex2 implements Runnable{  // class Thread_Ex2 extends Thread { 식으로 했었음  
	 // 하단에서 run() 메써드를 실행하라는 경고
	int temp[];
	public Thread_Ex2() {  // 생성자 메써드 사용
		temp = new int[10];
		for (int i = 0; i < temp.length; i++) {
			temp[i] = i;
			
		}
	}
//@Override
public void run() {                    //이 부분을 기술하니까 상단의 경고가 없어짐
	for (int i = 0; i <= temp.length; i++) {
		try {
			Thread.sleep(1000);
		} catch (Exception 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); //Thread_Ex2 te2 = new Thread_Ex2();
		te2.start();
	}

}

 


Deamon 쓰레드

데몬쓰레드는 다른 일반 쓰레드의 작업을 돕는 보조하는 역할을 수행하는 쓰레드이다. 함께 구동중인 일반 스레드가 종료되면 데몬 스레드도 함께 종료된다.

예를  들어 문서를 작성하는 도중에 3초간격으로 자동 세이브가 필요한 경우 이런 데몬쓰레드를 사용할 수 있다.

 

-쓰레드의 Join 기능: 하나의 쓰레드가 끝난 뒤 다시 이어서 (새로운 |기존 계속) 작업하는 것이 Join이다.

 

 

package java12;
public class Test01 implements Runnable{// ~extends Thread { , 추상 메써드 필요
	static boolean autoSave = false;  //불리언 변수 생성과 초기값 설정
	@Override
	public void run() {
		while (true) {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
			}
			if (autoSave == true) 
			 System.out.println("자동 저장됩니다.");	
			
		}
	}

	public static void main(String[] args) {
		Test01 dm = new Test01();
		Thread th = new Thread(dm); 
		th.setDaemon(true);   // 추상 메써드
		th.start();           // 추상 메써드, 내부적으로 run() 자동 실행
		for (int i = 0; i <= 15; i++) {
			try {
				Thread.sleep(1000);  // 1초
			} catch (InterruptedException e) {
//				e.printStackTrace();
			}
			System.out.println();
			if (i==3) { //3초 뒤에 자동 세이브
				autoSave = true;
				
			}
			System.out.println("프로그램 종료");
		}
	}


}

 

package java12;


public class Test02 implements Runnable {
	@Override
	public void run() {
		System.out.println("run()");
		first();
		
	}
	public void first() {
		System.out.println("first()");
		second();
	}
	public void second() {
		System.out.println("second()");
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("메인 클래스 시작");
		Test02 tj = new Test02();
		Thread th = new Thread(tj);
//		Runnable th = new Test02();
		th.start();  // 추상 메써드 
		try {
			th.join();// 추상 메써드 
		} catch (Exception e) {
			
		}   
		System.out.println("메인 클래스 종료");
	}
}

 

 

Q1)키보드에서 숫자를 입력받고 쓰레드에서 입력받은 숫자가 1씩 감소하다가 0이 되었을때 "종료"라는 메시지와 함께 쓰레드를 빠져나오게 하시오.

package java12;

import java.util.Scanner;

public class Test03 implements Runnable {
	private int n;
	public  Test03(int n) {  //Test03 생성자 
		this.n = n;
	}

	public static void main(String[] args) {
		System.out.println("Enter any Integer :");
		Scanner scan = new Scanner(System.in);
//		int n = scan.nextInt();
		Test03 tc = new Test03(scan.nextInt());  //Test03() 생성자를 호출하면서 객체 생성
		Thread th = new Thread(tc);
		th.start();
	}

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

}

 


쓰레드 동기화(Synchronized)

 

휴댜폰으로 음악이나 영상을 동기화 하게 되면, 동기화 끝날때까지 휴대폰은 다른 작업을 할 수가 없는데 이런 구조가 바로 동기화 쓰레드의 구조이다. 두개 이상의 쓰레드가 하나의 자원을 공유할 경우 동기화 문제가 발생된다. 변수는 하나인데 두 개의 쓰레드가 동시에 한 변수의 값을 변경하려고 하면 오류가 발생한다. 이를막기 위해서 내가 컴퓨터로 작업을 하다가 잠시 자리를 비운 사이 내 작업이 끝날때까지 다른 사람이 손대지 못하도록 컴퓨터를 잠가둘 필요가 있다.

 

이처럼 특정 쓰레드들이 공유하는 한 개의 자원이 사용 중일 때 이 작업이 끝날 때까지 다른 쓰레드가 이 자원을 굥유해서 작업하지 못하게 할 때 동기화가 필요하다.

 

 

'JAVA' 카테고리의 다른 글

[JAVA] wait()과 notify()  (0) 2022.08.02
[JAVA] getter와 setter  (0) 2022.08.02
[JAVA] 예외 처리  (0) 2022.08.01
[JAVA]인터페이스, 열거형, 내부클래스  (0) 2022.07.29
[JAVA]Object, 제네릭 클래스, 추상 메써드  (0) 2022.07.28

댓글