본문 바로가기
JAVA

Thread

by EUN-JI 2023. 8. 30.

★ Thread: 쓰레드를 이용하면 한 프로세스 내에서 (동시에 여러 작업을 처리하는 문법)

:  extends Thread       //쓰레드 상속하면 run 메서드를 구현해야함.

:  start()로 쓰레드를 실행한다.

 

★ Process: 동작하고 있는 프로그램

 

★ Task 

작업단위

 

★ (작업자,사장 ) Main Thread

 

 

★ (상속 :일꾼, 직원) Thread -  run() 메서드 작업 수행.

 

 

Thread 상속

Runnable구현

 My Thread

비동기. 동기방식

 

extends Thread = run override

 : sleep (try-catch문) (5000); 5초간격으로 

main Thread   = start

ThreadA a = new ThreadA();

a.start();    //자동으로 run 메소드가 발동함. 

ThreadB b = new ThreadB();

b.start();    //자동으로 run 메소드가 발동함. 

 

 

public class Main {

	public static void main(String[] args)  {
		
		//Thread(스레드)  : 동시에 여러 작업을 처리하는 기법    **프로세스 쓰레드 차이점 면접**
		//1. Process    : 실행중인 하나의 자바 프로그램 [본인만의 메모리영역을 가짐(Method, Stack, Heap)]
		//  -프로세스간에는 자원공유 불가
		//2. Thread     : 하나의 프로세스안에서 동작하는 일꾼(직원)같은 개념
		//  - 하나의 프로세스안에서 스레드간에는 *자원공유 가능*
		
		//즉, Thread는 작성되어 있는 코드를 읽어서 실행하는 녀석으로 생각하면 됨.
		//하나의 프로세스에는 적어도 하나의 스레드가 기본적으로 존재하며 이 스레드를 Main Thread 라고 부름(사장같은 개념)
		
		//동시에 했으면 하는 작업 2개 구현
		//ex) 파일 다운로드 하면서 음악재생 or 채팅하기 etc...
		
		
		//아직 네트워크는 배우지 않았으니.. 그냥 느낌만..
		
		//20개의 파일을 다운로드하는 작업..
//		for(int i=0; i<20; i++) {
//			System.out.println(i+ "번 파일 다운로드 중...");
//			
//			//강제로 시간이 걸리는 느낌 가지도록..
//			for(int k=0; k<2000000000; k++) {
//				new String("aaa");
//				new String("aaa");
//				new String("aaa");
//				new String("aaa");
//			}
//		}
		
		//20개의 음악을 재생하는 작업..
//		for (int i=0; i<20; i++) {
//			System.out.println(i+"번 음악 재생~~");
//			for(int k=0; k<2000000000; k++) {
//				new String("aaa");
//				new String("aaa");
//				new String("aaa");
//				new String("aaa");
//			}
//		}
		//Main Thread가 혼자서 위 코드를 읽어서 실행하기에
		//동시에 실행되지 않고 차례대로 실행됨.
		
		//동시에 하려면?? 별도의 직원(Thread의 능력을 가진)객체가 필요함.
		//이 직원이 해야할 작업(Task)을 작성해놓은 클래스를 설계
		// 직원채용- Thread 객체 생성
		ThreadA a= new ThreadA();
		//a.run(); //직접run()메소드를 호출하면 이 작업은 Main Thread가 함
		//스레드의 run()메소드 실행은 반드시 start()라는 명령으로 실행해야 함 ***********
		a.start(); //자동으로 run()메소드가 발동함
		
		ThreadB b=new ThreadB();
		b.start();
		//Main Thread도 별도의 작업을 동시에 수행하고 싶다면.
		for(int i =100; i<120; i++) {
			String name = Thread.currentThread().getName();
			System.out.println(name+ ":" + i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}//main method..
	

}//main class..

//파일 다운로드 작업을 수행하는 직원 클래스 - Thread의 능력을 가진..
class ThreadA extends Thread{
	//모든 Thread클래스의 작업은 반드시 이 메소드안에서 작성 해야만 함.
	//Thread클래스 안에 원래 있는 run()메소드를 오버라이드 해서 사용해야만 함.
	@Override 
	public void run() {
		for(int i=0; i<20; i++) {
			System.out.println(i+ "번 파일 다운로드 중...");
			String name= Thread.currentThread().getName();
			System.out.println(name+ ":" + i+"번 다운로드~");
			
			//현재 스레드를 0.5초간(1000ms) 잠재우기
			try {
				Thread.sleep(500);// sleep **
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
			//강제로 시간이 걸리는 느낌 가지도록..
//			for(int k=0; k<2000000000; k++) {
//				new String("aaa");
//				new String("aaa");
//				new String("aaa");
//				new String("aaa");
//			}
		}
		
	}
	
}


//음악 재생 작업을 수행하는 직원 클래스 - Thread의 능력을 가진..
class ThreadB extends Thread{
	@Override
	public void run() {
		for (int i=0; i<20; i++) {
			System.out.println(i+"번 음악 재생~~");
			String name = Thread.currentThread().getName();
			System.out.println(name+ ":" + i+"번 음악재생~~");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
//			for(int k=0; k<2000000000; k++) {
//				new String("aaa");
//				new String("aaa");
//				new String("aaa");
//				new String("aaa");
//			}
		}
	}
}
--------------------------------------------------------------------------------------------
public class RunnableTest {

	public static void main(String[] args) {
		
		
		//Thread 만드는 2가지 방법
		//1. Thread클래스를 상속하는 클래스를 설계하여 사용
		//2. Runnable인터페이스를 구현하는 클래스를 설계하여 사용
		
		//인터페이스가 존재하는 이유가 뭘까?
		//다중 상속의 효과를 보고 싶어서..
		//Person클래스의 능력을 가지면서.. 동시에 스레드의 능력도 보유한 객체를 생성
		PersonThread pt=new PersonThread();
		pt.name="sam";
		pt.age=20;
		
		//스레드의 기능 run()메소드가 실행되도록..
		//pt.stat(); //error -- Runnable은 start()기능이 없음.
		//pt.run();//이 작업은 Main Thread가 수행함.
		
		//Runnable로 만든 스레드객체는 start를 시켜주는 별도의
		//trigger용 Thread객체가 필요함.
		Thread t= new Thread(pt);  //생성자에 Runnable객체를 전달
		t.start();  //대신 start.. 생성자로 받은 Runnable객체의 run()메소드가 발동됨
		System.out.println("메인 스레드 작업");
		
		//클래스를 만들어놓으면 언제든 new만 쓰면 여러개의 객체를 만들 수 있음.
		PersonThread pt2= new PersonThread();
		PersonThread pt3= new PersonThread();
		
		//Runnable 인터페이스를 구현한 클래스의 이름을 매번 정하는것이 
		//은근 짜증 .. 만약 그 클래스가 1번만 객체로 만들어서 사용될것이라면 
		//별도의 클래스.Jave파일로 만드는 것이 더 짜증 !!~비효율적임.
		//그래서 1회용 설계도를 이용하면 이를 해결함. 
		//객체를 생성하면서 설계하는 문법을 이용할 것임.
		
		//이름이 없는 클래스 -- 익명클래스 라고 부름   **GUI에서 많이 사용!!
		Runnable r=new Runnable() {
			@Override
			public void run() {
				System.out.println("runnable...");
			}
		};
		
		Thread tt = new Thread(r);
		tt.start();
		//위 코드를 더 줄여서..
		Thread ttt= new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
			System.out.println("익명 클래스...");	
			}
		});
		ttt.start();
		//GUI에서 많이 사용함 **
		new Thread() {
			public void run() {
				System.out.println("스레드 상속한 익명클래스");
			}
		}.start();
	}

}//main

class PersonThread extends Person implements Runnable{
	@Override
	public void run() {
		for (int i=0; i<5; i++) {
			System.out.println(name+ " : " + age);
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
}

class Person{
	String name;
	int age;
	
}
--------------------------------------------------------------------------------------------
public class RaceHorseTest {

	public static void main(String[] args) {
		Horse h1 = new Horse("캐논");
		Horse h2 = new Horse("천리안");
		Horse h3 = new Horse("적토마");
		
		System.out.println("경주시작");
		
		h1.start();
		h2.start();
		h3.start();
		
		h1.setPriority(10);  //높은 우선권
		h2.setPriority(2);
	}

}

class Horse extends Thread{
	
	String name;
	
	//생성자
	public Horse(String name) {
		this.name =name;
		
	}
	
	@Override
	public void run() {
		for(int i=0; i<20; i++) {
			System.out.println(name +": 다그닥!!다그닥!!");
			
			try {
				Thread.sleep(400);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}//for
		System.out.println(name+ "도착");
	}
}
--------------------------------------------------------------------------------------------


public class SunchronizedTest {

	public static void main(String[] args) {
		//동기화 처리 - Thread를 사용하는 것은 비동기 처리를 한다는 것임.
		//동기  (Synchronize)  : a 작업이 끝나면 b 작업 하는 것. 
		//비동기 (Asynchronize) : a 작업을 할때 b 작업도 동시에 하는 것.
		
		//계좌 객체
		Account acc= new Account();
		
		//acc계좌에 100원을 입금하고 싶은 고객1 - Thread
		CustomerThread t1= new CustomerThread(acc);
		
		
		//acc계좌에 100원을 입금하고 싶은 고객2 - Thread
		CustomerThread t2= new CustomerThread(acc);
		
		//비슷한 시점에 둘다 100원을 입금 동작 실행!
		t1.start();  //run()메소드 자동 발동!
		t2.start();
		
		
	}

}

//은행계좌 클래스
class Account{
	
	int money= 0;
	
	//입금기능 - 동기화처리 되지 않은 기능
	//이 add()기능을 동기화 처리하지 않으면 여러 Thread가 동시에 기능을 발동하여 문제를 야기할 수 있음.
	//이를 해결하기 위해 동기화 처리를 함. 방법 2가지 존재함.
//	void add(int m) {
//		System.out.println("입금 작업을 시작합니다.");
//		
//		String name= Thread.currentThread().getName();
//		System.out.println(name + " - 현재 잔액 : " + money);
//		
//		//입금기능 코드
//		money= money+m;
//		//일부러 전산처리 시간을 가정하여 오래걸리는 작업 코드.
//		for (long i=0;i<45000000000L; i++) {
//			new String("aaa");
//		}////////////////////////////////시간버는 용 
//		
//		System.out.println(name+ " - 입금 후 잔액 : "+ money);
//		System.out.println();
//		
//	}
	
		//방법1. 이add()기능 메소드 자체를 동기화 처리
//	synchronized void add(int m) {
//		System.out.println("입금 작업을 시작합니다.");
//		
//		String name= Thread.currentThread().getName();
//		System.out.println(name + " - 현재 잔액 : " + money);
//		
//		//입금기능 코드
//		money= money+m;
//		//일부러 전산처리 시간을 가정하여 오래걸리는 작업 코드.
//		for (long i=0;i<45000000000L; i++) {
//			new String("aaa");
//		}////////////////////////////////시간버는 용 
//		
//		System.out.println(name+ " - 입금 후 잔액 : "+ money);
//		System.out.println();
//		
//	}
	
	//방법2. 동기화 블럭
	void add(int m) {
		System.out.println("입금 작업을 시작합니다.");
		
		//동기화 블럭(보호할 것)  -Thread의 기능을 감소시킴 synchronized()   :rock걸렸다.
		synchronized(this) {
			String name= Thread.currentThread().getName();
			System.out.println(name + " - 현재 잔액 : " + money);
		
		//입금기능 코드
		money= money+m;
		//일부러 전산처리 시간을 가정하여 오래걸리는 작업 코드.
		for (long i=0;i<45000000000L; i++) {
			new String("aaa");
		}////////////////////////////////시간버는 용 
		
		System.out.println(name+ " - 입금 후 잔액 : "+ money);
		System.out.println();
			
		}
		
		
	}
	
}




//계좌에 돈을 입금하는 동작을 수행하는 Thread클래스 설계
class CustomerThread extends Thread{
	
	Account acc;
	
	//constructor
	public CustomerThread(Account acc) {
		this.acc= acc;
	}
	
	
	@Override
	public void run() {
		acc.add(100);  //100원을 입금하는 동작 수행
	}
}
-------------------------------------------------------------------------------------------

public class ThreadControlTest {

	public static void main(String[] args) {
		//게임만들때 쓰임. Thread많이 사용.**
		//타이어조립 직원객체 생성
		CThread t=new CThread();
		t.start();   //Thread는 딱 1번만 start()실행 가능. ******
		//t.start();
		//t.start();//error 
		
		//3초 후에 휴식.. 즉, 스레드의 일시정지
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//t.wait();  //직접 wait()을 권장하지 않음.
		t.pauseThread();
		//3초 후에 휴식 끝. 작업개시 .. 즉, 스레드 이어하기
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//wait()으로 일시정지된 스레드를 깨워야 함.
		//t.notify();  //직접 notify()를 권장하지 않음
		//t.resumeThread();
		
		//3초 후에 퇴근..즉, 스레드 종료
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//t.stop(); //동작은 되지만 권장하진 않는 문법.
		//강제로 스레드를 멈추지 말고 자연스럽게
		//run()메소드가 종료되도록 해야함.
		//while문에 의해 run()이 안끝나고 있으니
		//while문만 종료시키면 됨 
		//while문의 반복조건 변수인 isRun을 false로 변경
		//t.isRun= false;
		//객체지향프로그래밍 방법론에서는 가급적 객체의 멤버변수는
		//직접 제어하지 않는 것을 권장함. 
		//메소드를 이용하여 멤버변수의 값 변경을 권장함. 
		t.stopTherad();
		
	}//Main method..

}//class..

//특정 작업을 수행하는 직원(Thread) 클래스
class CThread extends Thread{
	
		private boolean isRun= true;
		private boolean isWait= false;
		
	
			@Override
			public void run() {
				//for문은 수행갯수를 알수있을때 사용
				while(isRun) {
					//작업 Task 단위
					System.out.println("1번 타이어 조립");
					System.out.println("2번 타이어 조립");
					System.out.println("3번 타이어 조립");
					System.out.println("4번 타이어 조립");
					System.out.println();
					
					if(isWait) {
						//필수로 동기화 처리를 해야함.
						synchronized (this) {
							try {
								wait();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					
					}
					
					
					
					//반복문이 너무 빨리 처리되어서..1초간 잠시 정지
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
				}//while..
					
				System.out.println("\n퇴근!!\n");
				
			}//run method..
			
			//Thread를 안전하게 종료하는 기능메소드 정의
			public void stopTherad() {
				this.isRun= false;
			}
	//스레드를 안전하게 일시정지 하는 기능메소드 정의
			public void pauseThread() {
				this.isWait= true;
			}
			//스레드의 일시정지상태를 안전하게 깨우기
			public void resumeThread() {
			
			
				this.isWait=false;
			
				//반드시 동기화 처리를 같이 해야 함
			synchronized(this) {
				this.notify(); //wait된 스레드를 깨움.
			}
			}
			
}//CThread class..