본문 바로가기

웹_프론트_백엔드/JAVA

2020.09.22(JAVA_심화반)

1. 복습
1) Set : 집합, 중복이 없다, 검색

   Iterator() : 반복자

2) Map : 키 [해쉬테이블] 값
   KeySet() : Set
   values() : Collection

 

 

2. 프로그램 > 프로세스 > Thread(쓰레드) > 작업
1) 프로그램
 : 소스코드로 잘 짜여진 틀.
   더블 클릭하면 실행되는 것.
   아직 실행되지 않음.

2) 프로세스
 : 실행된 프로그램.
   운영체제로부터 시스템 자원을 할당받는 작업의 단위.
   자바에서는 JVM으로부터 할당 받는다.


3) 쓰레드
 : 프로세스의 특정한 수행 경로

   하나의 처리 경로를 가지느냐, 여러 개의 처리 경로를 가지느냐에 따라서 단일 쓰레드와 멀티 쓰레드로 나누어진다.

 

 

3. 단일 쓰레드와 멀티 쓰레드
1) 단일 쓰레드
 : 직렬적이다.
   첫번째 작업이 모두 완료된 후 다음 작업을 시작하기 때문에 동시에 모든 작업이 문제를 발생시키지 않는다.
   따라서 보다 안정적이다.
   설계가 어렵지 않다.

   [예] 다운로드 프로그램 이용하여 여러 개 다운로드시 순차적으로 위의 다운로드가 끝나면 다음 다운로드가 되는 것,

         매표소


2) 멀티 쓰레드

 : 병렬적이다.
   하나의 프로세스를 동시에 처리하는 것처럼 보이지만, 사실은 매우 짧은 단위로 분할해서 차례로 처리한다.
   한 개의 처리경로를 여러 개로 나누어 동시 작업이 가능하다.
   웹 서버가 대표적인 멀티 쓰레드 응용프로그램이다.

   [예] 다운로드 프로그램 이용하여 여러 개 다운로드시 동시에 다운로드 되는 것,

         음식점, 게임 스킬(스킬 발동 작업 + 쿨타임 돌아가는 작업)

 

   [장점]
   효율성 증가
   처리량 증가
   처리비용 감소


   [단점]
   복잡하고 설계가 어려움
   자원의 공유 문제
   하나의 쓰레드 문제 발생시 전부 문제 발생
   설계가 굉장히 어려움
   교착상태(DeadLock)

 



4. 교착상태
 : 멀티 쓰레드 중 쓰레드 간에 대기 상태가 종료되지 않아서 무한정 대기만 하는 비정상적인 상태

   [교착상태 해결방법]
   하나의 쓰레드를 종료시킨다.
   모든 쓰레드를 깨워준다( notifyAll() )

 

 

5. 쓰레드 구현 방법
1) Thread 클래스
 : Thread 클래스를 상속받고 run메소드를 재정의하여 쓰레드를 구현한다.

2) Runnable 인터페이스
 : Runnable 인터페이스를 지정받고 run메소드를 재정의하여 쓰레드를 구현한다.

 


6. 멀티 쓰레드를 구현할 때에는 운영체제에게 스케줄링을 해야 한다.
   자바에서 멀티 쓰레드를 스케줄링 해주는 메소드는 Thread클래스에 있는 start()라는 메소드이다.

 


7. 쓰레드 예제

** com.lec.java.thread > Thread1.java

package com.lec.java.thread;

public class Thread1 extends Thread {
	private String data;
	
	public Thread1() {;}
	public Thread1(String data) {
		super();
		this.data = data;
	}

	// 멀티 쓰레드에서는 run() 메소드 안에 구현되는 것을 자원이라고 한다.
	// 그리고 이러한 자원은 JVM이 할당해준다.
	@Override
	public void run() {
		for(int i = 0; i < 10; i++) {
			System.out.println(data);
			
			// 멀티 쓰레드 구현했으나 너무 빨라서 우리 눈에 확인할 수 없음
			// 그래서 시간을 늦춰줌으로써 우리 눈으로 확인할 수 있게 된다!
			try {Thread.sleep(1000);} catch (InterruptedException e) {;}
		}
	}
}

 

** com.lec.java.thread > Thread2.java

package com.lec.java.thread;

public class Thread2 implements Runnable {
	private String data;
	
	public Thread2() {;}
	public Thread2(String data) {
		super();
		this.data = data;
	}

	@Override
	public void run() {
		for(int i = 0; i < 10; i++) {
			System.out.println(data);
			// [다시 한 번 명심!]
			// Thread.sleep()을 쓰는 이유는 육안으로 확인하기 위해서는
			// 속도를 늦춰줘야하기 때문에!
			try {Thread.sleep(1000);} catch (InterruptedException e) {;}
		}
	}
}

 

** com.lec.java.thread > ThreadTest.java

package com.lec.java.thread;

public class ThreadTest {
	public static void main(String[] args) {
		// 1. Thread 클래스
		// Thread 클래스를 상속받고 run 매소드를 재정의하여 쓰레드를 구현한다.
		//Thread1 t1 = new Thread1("★");
		//Thread1 t2 = new Thread1("♥ ");
		
		// 아래의 방법은 단일 쓰레드이다.
		// run()이라는 메소드를 main 메서드에서 썼기 때문에 단일 쓰레드이다.
		// 그렇기 때문에 t1이 끝나고 t2가 시작된다.
		//t1.run();
		//t2.run();
		
		// 멀티 쓰레드를 구현할 때에는 운영체제에게 스케줄링을 해야한다.
		// 자바에서 멀티쓰레드를 스케줄링 해주는 메소드는 Thread클래스에 있는 start()라는 메소드이다.
		
		// 여기서 질문..?
		// 나는 run()을 재정의했는데 start()가 내가 재정의한  run()을 어떻게 알고 실행을 해줄까?
		
		// start()는 자식에서 재정의된 run()이라는 메소드를 알고 있고
		// 현재  Thread 타입이 아닌 그 자식인 t1에서 start()를 쓰게 된다.
		
		// 즉, 자식에서 start()를 쓰면 자식에서 재정의된 값이 반영이 되는 casting 개념을 생각하면 된다.
		//t1.start();
		//t2.start();
		
		// 그런데도 결과는 멀티 쓰레드의 결과가 나오지 않았다...!
		// 스케줄링도 등록도했는데 왜 그럴까?
		
		// 그 이유는 너무 빨라서 우리 눈에 적용이 안된 것 처럼 보인 것이다.
		// 그래서 Thread.sleep()을 이용하여 시간을 멈춰주면
		// 비로소 우리 눈에 보이게 된다!
		
		// 2. Runnable 인터페이스
		// Runnable 인터페이스를 지정받고 run메소드를 재정의하여 쓰레드를 구현한다.
		Runnable t1 = new Thread2("!");
		Thread2 t2 = new Thread2("?");
		
		// start() 메소드를 사용하기 위해 생성자 Thread(Runnable target) 해주기!
		// Thread2 implements Runnable
		// up-casting 개념 이용...!
		Thread thread1 = new Thread(t1);
		Thread thread2 = new Thread(t2);
		
		thread1.start();
		thread2.start();
		
		// 멀티 쓰레드일때는 우선순위에 따라 출력되기 때문에 뭐가 먼저 나올지 알 수 없다.
		
		// 또한 잘못된 우선순위로 짠 로직은 큰 오류를 발생시키기 때문에
		// 쓰레드마다 우선 순위를 결정할 수 있으나 이번 수업에서 배우지 않을 예정!
		
		// join()을 사용한 쓰레드가 모두 끝날 때까지 다른 쓰레드는 대기한다.
		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {;}
		
		System.out.println("메인 쓰레드 종료");
	}
}

 

 

8. 쓰레드 내용 정리

1) 우린 알게 모르게 쓰레드를 써왔다.

   (어떤 쓰레드를 써왔는데? 메인 쓰레드, 그렇기 때문에 메인 메소드 내에서만 작업이 가능한 것이다)

 

2) Thread 클래스를 이용한 단일 쓰레드 구현

 

3) 멀티 쓰레드를 스케줄링 해주는 start() 메소드를 이용했으나 단일 쓰레드를 사용한 것과 같은 출력이 되었다.

 

4) 그 이유는 너무 빨라서 육안으로 보이지 않았던 것이다.
   육안으로 볼 수 있도록 Thread.sleep()을 이용하여 시간을 멈춰주면 비로소 멀티 쓰레드가 육안으로 확인 가능하다.

 

5) Runnable 인터페이스를 이용하여 멀티 쓰레드 구현

 

7) 멀티 쓰레드에 우선순위가 있는데 메인 쓰레드가 가장 높다.

 

8) 그러나 join() 메소드를 이용, join() 메소드를 사용한 쓰레드가 모두 끝날때까지 다른 쓰레드가 대기하여

   우선 순위 높은 메인 쓰레드를 제일 마지막에 출력 했다.

 

 

9. 쓰레드 문제
   동물원에 동물 3마리가 있다. 

   각 동물은 울음소리가 다르고 2마리의 동물은 동시에 운다.
   나머지 1마리 동물은 2마리 동물이 모두 울고 나서 마지막에 운다.
   클래스는 총 2개를 사용하고 하나의 클래스에는 main메소드가 있다.
   Runnable 인터페이스로 멀티쓰레드를 구현하고 반드시 join()을 사용한다.
   ※ 각 동물은 10번씩만 운다. 제한시간 : 15분

 

1) 내코드

** com.lec.java.practice.me > Animal.java

package com.lec.java.practice.me;

public class Animal implements Runnable {
	private String crying;
	
	public Animal() {;}
	public Animal(String crying) {
		super();
		this.crying = crying;
	}

	@Override
	public void run() {
		for(int i = 0; i < 10; i++) {
			System.out.println(crying);
			try {Thread.sleep(1000);} catch (InterruptedException e) {;}
		}
	}
}

 

** com.lec.java.practice.me > Zoo.java

package com.lec.java.practice.me;

public class Zoo {
	public static void main(String[] args) {
		Runnable dog = new Animal("멍멍");
		Runnable cat = new Animal("야옹");
		Runnable tiger = new Animal("어흥");
		
		Thread threadDog = new Thread(dog);
		Thread threadCat = new Thread(cat);
		Thread threadTiger = new Thread(tiger);
		
		threadDog.start();
		threadCat.start();
		
		try {
			threadDog.join();
			threadCat.join();
		} catch (InterruptedException e) {;}
		
		threadTiger.start();
	}
}

 

2) 강사님 코드
** com.lec.java.practice.teacher > Animal.java

package com.lec.java.practice.teacher;

public class Animal implements Runnable {
	private String sound;
	
	public Animal() {;}
	public Animal(String sound) {
		super();
		this.sound = sound;
	}

	public void makeSound() {
		for(int i = 0; i < 10; i++) {
			System.out.println(this.sound);
			try {Thread.sleep(1000);} catch (InterruptedException e) {;}
		}
	}
	
	@Override
	public void run() {
		makeSound();
	}
}

 

** com.lec.java.practice.teacher > Zoo.java

package com.lec.java.practice.teacher;

public class Zoo {
	public static void main(String[] args) {
		Animal tiger = new Animal("어흥");
		Animal lion = new Animal("크르릉");
		Runnable parrot = new Animal("안녕하세요.");
		
		Thread tiger_thread = new Thread(tiger);
		Thread lion_thread = new Thread(lion);
		Thread parrot_thread = new Thread(parrot);
		
		tiger_thread.start();
		lion_thread.start();
		
		try {
			tiger_thread.join();
			lion_thread.join();
		} catch (InterruptedException e) {;}
		
		parrot_thread.start();
	}
}



10. 다음 수업 내용 : 동기화, 파일 입출력

'웹_프론트_백엔드 > JAVA' 카테고리의 다른 글

2020.09.29(JAVA_심화반)  (0) 2020.10.01
2020.09.24(JAVA_심화반)  (0) 2020.09.26
2020.09.18(JAVA_심화반)  (0) 2020.09.21
2020.09.17(JAVA_심화반)  (0) 2020.09.18
2020.08.27(JAVA_심화반)  (0) 2020.08.28