210803 화
<스레드>
: 프로세스 내에서 할당된 자원을 이용해 실제 작업을 수행하는 작업 단위
-모든 프로세스는 하나 이상의 스레드를 가지며 각각 독립적인 작업 단위를 가짐
*프로세스
: 간단한 의미로 실행중인 프로그램
-프로세스는 프로그램이 실행 될 때 마다 개별적으로 생성
-하나의 프로세스는 프로그램을 수행함에 있어 필요한 데이터와 메모리 등의 할당 받은 자원, 그리고 하나 이상의 스레드로 구성된다
1. 메인 스레드
-모든 자바 프로그램은 메인 스레드가 main() 메소드를 실행하며 시작.
-main() 메소드의 첫 코드부터 아래로 순차적으로 실행되고, return을 만나면 종료.
-필요에 의해 작업 스레드들을 만들어서 병렬 코드를 실행 가능.(멀티 스레드를 이용한 멀티 태스킹)
2. 프로세스 종료
-싱글 스레드의 경우 메인 스레드가 종료되면 프로세스도 종료되지만, 멀티 스레드의 경우 실행중인 스레드가 하나라도 있다면 프로세스가 종료되지 않음
*멀티 프로세스 VS 멀티 스레드
1) 멀티 프로세스 : 각각의 프로세스를 독립적으로 실행하는 것.
예) 운영체제
2) 멀티 스레드 : 하나의 프로세스 내에서 여러 스레드가 동시에 작업을 수행하는 것.
예) 하나의 프로그램 안의 일꾼들.
3. 싱글 스레드와 멀티 스레드
4. 스레드 생성
1) Thread 클래스를 상속받는 방법
2) Runnable 인터페이스를 구현하는 방법
5. 멀티스레드 장점
1) 자원을 보다 효율적으로 사용 가능
사용자의 대한 응답성 향상
2) 애플리케이션의 응답성 향상
3) 작업이 분리되어 코드가 간결해짐
4) CPU 사용률 향상
6. 멀티스레드 단점
1) 동기화(Synchronization)에 주의해야 함
2) 교착상태(dead-lock)가 발생하지 않도록 주의해야 함
3) 프로그래밍 시 고려해야 할 사항이 많음
7. 스레드 스케쥴링
: 스레드 개수가 코어의 수보다 많을 경우 스레드를 어떤 순서로 동시성을 실행할 것인가를 결정하는 것.
-스케쥴링에 의해 스레드들은 번갈아가며 run()메소드를 조금씩 실행.
1) 우선 순위 방식
(1) 우선 순위가 높은 스레드가 작업 시간을 더 많이 가지도록 하는 스케쥴링 방식
(2) 스레드에 1 ~ 10 까지 우선 순위 번호 부여 가능(번호가 높을수록 우선 순위가 높음)
(3) 스레드 생성 시 우선 순위 기본값은 5
2) 순환 할당 방식
(1) 시간 할당량(Time Slice)를 정하여 하나의 스레드를 정해진 시간만큼 실행시키는 방식
(2) JVM에 의해 정해짐(코드로 제어 불가능)
8. 데몬 스레드
1) 특징
(1) 다른 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드
(2) 데몬 스레드 이외의 스레드들이 모두 종료되면 데몬 스레드는 강제적으로 종료된다
2) 데몬 스레드 생성
-데몬 스레드가 될 스레드의 레퍼런스 변수에 setDaemon(true)를 호출하여 생성
단,start() 메소드 호출 전에 setDeamon(true)를 호출해야 한다. -> IllegalThreadStateException이 발생
9. 스레드 컨트롤
: 실행중인 스레드의 상태를 제어하기 위한 것. 효율적이고 정교한 스케쥴링을 위한 스레드 상태를 제어하는 기능.
<Part01_thread>
1. Thread
1) thread1
package com.kh.part01_thread.thread;
//스레드 생성 방법1. Thread 클래스 상속 받는 방법
public class Thread1 extends Thread {
//상속 처리 후 run 메소드 오버라이딩
@Override
public void run() {
//해당 스레드를 통해 작업하고자 하는 코드를 작성한다
for(int i = 1 ; i <= 10 ; i ++) {
//getName : 스레드의 이름을 리턴(설정하지 않았으므로 시스템에서 부여한 이름 리턴)
System.out.println(getName() + "[" + i + "]");
}
}
}
2) thread2
package com.kh.part01_thread.thread;
//스레드 생성 방법2. Runnable 인터페이스 구현 방법
public class Thread2 implements Runnable {
@Override
public void run() {
//해당 스레드를 통해 작업하고자 하는 코드 작성
for(int i = 1 ; i <= 10 ; i++) {
//Thread 클래스를 상속 받은 것이 아니여서
//바로 Thread의 getName 메소드는 호출 불가능
//Thread 클래스의 static 메소드 currentThread로
//현재 실행 중인 스레드 객체 반환 받아 호출
System.out.println(Thread.currentThread().getName() + "[" + i + "]");
}
}
}
2. Run
package com.kh.part01_thread.run;
import com.kh.part01_thread.thread.Thread1;
import com.kh.part01_thread.thread.Thread2;
public class ThreadRun {
public static void main(String[] args) {
//1. Thread를 상속 받은 클래스의 객체 생성 후 실행
Thread1 th1 = new Thread1();
//th1.run(); => 이런식으로 하면 안된다.
th1.start();
//모든 스레드는 독립적인 작업을 수행하기 위해 자신만의 호출 stack을 필요로 함
//start 메소드는 새로운 스레드가 작업을 실행하는데 필요한 호출 stack을 생성한 뒤
//생성 된 호출 stack에 run 메소드가 첫번째로 올라가게 함
//(run 바로 호출 시 main thread에서 실행 됨)
//답
// Thread-0[1]
// Thread-0[2]
// Thread-0[3]
// Thread-0[4]
// Thread-0[5]
// Thread-0[6]
// Thread-0[7]
// Thread-0[8]
// Thread-0[9]
// Thread-0[10]
//2. Runnable을 구현한 클래스의 객체 생성 후 실행
//Thread 객체를 새로 생성할 때 생성자의 파라미터로 Runnable 구현 클래스 객체 전달
Thread2 th2 = new Thread2();
Thread th = new Thread(th2);
//=> runnable을 thread로 보내준 뒤 start하는 방식(무조건)
th.start();
//답
// Thread-0[1]
// Thread-0[2]
// Thread-0[3]
// Thread-0[4]
// Thread-0[5]
// Thread-0[6]
// Thread-0[7]
// Thread-0[8]
// Thread-0[9]
// Thread-0[10]
//3. 스레드 재호출 //=> 불가능
//th1.start();
//이미 start는 수행을 하고 행위를 끝냈는데 다시 수행하라고 하니까 에러가 남
//한 번 종료 된 스레드는 다시 호출 불가능 함
}
/*
* Runnable 인터페이스와 Thread 클래스의 차이점
* 1) Runnable 인터페이스
* : 스레드화 될 수 있는 메소드를 정의하는 run 메소드를 가지고 있음
* 2) Thread 클래스
* : Runnable 인터페이스를 상속하고 있으며 스레드를 활성화하는 start 메소드를 가지고 있음
* : Thread 상태를 제어할 수 있는 메소드를 가지고 있음
*/
}
<Part02_Scheduling>
1. Thread
1) Car
package com.kh.part02_scheduling.thread;
public class Car implements Runnable {
@Override
public void run() {
for(int i = 0 ; i < 150 ; i ++) {
System.out.println(i + " Car driving...");
//스레드를 지연시키는 메소드(밀리세컨단위) 1/1000초
try {
Thread.sleep(100); //0.1초 지연
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2) Plane
package com.kh.part02_scheduling.thread;
public class Plane implements Runnable {
@Override
public void run() {
for(int i = 0; i < 100 ; i++) {
System.out.println(i + " Plane flight...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3) Tank
package com.kh.part02_scheduling.thread;
public class Tank implements Runnable{
@Override
public void run() {
for(int i = 0 ; i < 100 ; i++) {
System.out.println(i + "Tank shooting...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2. Run
package com.kh.part02_scheduling.run;
import com.kh.part02_scheduling.thread.Car;
import com.kh.part02_scheduling.thread.Plane;
import com.kh.part02_scheduling.thread.Tank;
public class Run {
public static void main(String[] args) {
//스레드는 기본적으로 1~10의 우선 순위 중 5의 우선 순위를 가지고 있음
// => 우선 순위는 스레드를 생성한 스레드로부터 상속 받음(메인 스레드의 우선 순위가 5)
Thread t1 = new Thread(new Car());
Thread t2 = new Thread(new Plane());
Thread t3 = new Thread(new Tank());
//실행 순서가 매번 다른 이유는 각각의 스레드가 모두 같은 우선 순위를 가지고 있고
//운영체제(OS)에서 직접 임이의 스레드를 한번씩 호출해서 출력
//답, 끊임없이 답이 출력됨, 출력 순서는 지맘대로임
// 0 Car driving...
// 0Tank shooting...
// 0 Plane flight...
// 1 Car driving...
// 1 Plane flight...
// 1Tank shooting...
// 2 Plane flight...
// 2 Car driving...
// 2Tank shooting...
// 3 Car driving...
// 3 Plane flight...
// 3Tank shooting...
//각 스레드 별로 우선 순위 다르게 부여
t1.setPriority(1);
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(5);
t2.setPriority(Thread.NORM_PRIORITY);
t3.setPriority(10);
t3.setPriority(Thread.MAX_PRIORITY);
//답 : 여전히 우선 순위가 잘 적용 되지 않음
//=> CPU 멀티 코어 환경때문(꼭 멀티 코어를 써야할 환경이 아니기 때문)
// Car 우선순위 : 1
// Plane 우선순위 : 5
// Tank 우선순위 : 10
// 0 Car driving...
// 0 Plane flight...
// 0Tank shooting...
// 1 Plane flight...
// 1 Car driving...
// 1Tank shooting...
// 2 Plane flight...
// 2 Car driving...
// 2Tank shooting...
// 3Tank shooting...
// 3 Car driving...
// 3 Plane flight...
System.out.println("Car 우선순위 : " + t1.getPriority());
System.out.println("Plane 우선순위 : " + t2.getPriority());
System.out.println("Tank 우선순위 : " + t3.getPriority());
// 답
// Car 우선순위 : 5
// Plane 우선순위 : 5
// Tank 우선순위 : 5
//t1.setDaemon(true); //보조 스레드로 등록
// => 데몬 스레드는 다른 스레드의 작업을 돕는 보조적인 역할을 하는 스레드로
// 데몬 스레드 외의 스레드가 모두 종료 되면 강제 종료 된다
// ex) 가비지 콜렉터, 자동 저장, 화면 자동 갱신 등의 보조적인 기능에 활용
t1.start();
t2.start();
t3.start();
//System.out.println("======= main end ======");
//이 코드는 언제 출력 될까요?
//다른 스레드들이 끝나기 전에 메인 스레드가 종료 되어 버림
// Car 우선순위 : 1
// Plane 우선순위 : 5
// Tank 우선순위 : 10
// ======= main end ======
//지정한 스레드가 종료 될 때까지 현재 메인 스레드의 종료를 대기 시키려면?
try {
//Waits for this thread to die
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("======= main end ======");
// 141 Car driving...
// 142 Car driving...
// 143 Car driving...
// 144 Car driving...
// 145 Car driving...
// 146 Car driving...
// 147 Car driving...
// 148 Car driving...
// 149 Car driving...
// ======= main end ======
}
}
<Part03_Control>
1. Thread
1) Thread3
package com.kh.part03_control.thread;
public class Thread3 implements Runnable {
@Override
public void run() {
// B 작업
for(int i = 1 ; i <= 10 ; i++) {
try {
//sleep으로 1초씩 지연
Thread.sleep(1000);
//1~10초 카운트 출력
System.out.print(i + "초 ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2) Thread4
package com.kh.part03_control.thread;
public class Thread4 implements Runnable {
@Override
public void run() {
int cnt = 0;
//반복문 종료 조건 1.cnt < 10 => 10초 경과
//반복문 종료 조건 2. 해당 스레드의 interrupted 속성 값이 true일 때
//isInterrupted 메소드 : 스레드의 interrupted 속성값 반환
while(cnt < 10 && !Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(1000);
System.out.println(++cnt + "초");
} catch (InterruptedException e) {
//e.printStackTrace();
//스레드가 sleep 작업 처리 중 interrupt가 호출 되면
//interrupted 속성 값이 바뀌는 것이 아니라 InterruptedException이 발생함
//그래서 interrupted 속성 값은 여전히 false여서 while 반복문이 진행 되었음
//다시 interrupt 호출하여 interrupted 속성 값을 true로 변경한다.
System.out.println(cnt + "초에 카운트 종료");
Thread.currentThread().interrupt();
}
}
}
}
2. Run
package com.kh.part03_control.run;
import java.util.Scanner;
import com.kh.part03_control.thread.Thread3;
import com.kh.part03_control.thread.Thread4;
public class Run {
public static void main(String[] args) {
//1. 하나의 스레드로 사용자의 입력을 받는 작업과
//화면에 카운트를 출력하는 작업을 했을 때
// // A 작업
// Scanner sc = new Scanner(System.in);
// System.out.print("입력 : ");
// String input = sc.nextLine();
// System.out.print("입력한 값 : " + input);
//
// System.out.println();
// // B 작업
// for(int i = 1 ; i <= 10 ; i++) {
// try {
//sleep으로 1초씩 지연
// Thread.sleep(1000);
// //1~10초 카운트 출력
// System.out.print(i + "초 ");
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
//답
// 입력 : 3
// 입력한 값 : 3
// 1초 2초 3초 4초 5초 6초 7초 8초 9초 10초
//2. 두 개의 스레드로 사용자의 입력을 받는 작업과
//화면에 카운트를 출력하는 작업을 했을 때
//B작업은 별도의 스레드로 진행
// Thread th = new Thread(new Thread3());
// th.start();
//
// // A 작업
// Scanner sc = new Scanner(System.in);
// System.out.print("입력 : ");
// String input = sc.nextLine();
// System.out.print("입력한 값 : " + input);
//답
// 입력 : 1초 2초 부승관
// 입력한 값 : 부승관3초 4초 5초 6초 7초 8초 9초 10초
//3. 사용자의 입력이 있을 경우 카운트 종료 시키기
//interrupt 메소드 활용
//B 작업 수행 스레드 실행
Thread th = new Thread(new Thread4());
th.start();
//A 작업
Scanner sc = new Scanner(System.in);
System.out.print("입력 : ");
String input = sc.nextLine();
System.out.print("입력한 값 : " + input);
//사용자의 입력이 있을 겨웅 메인 스레드는 코드가 진행되므로
//동작 중인 스레드에 interrupt 호출해서 interrupted 값을 true로 변경함
//스레드에 현재 작업을 취소하라는 요청을 넣음
th.interrupt();
// 답
// 입력 : 1초
// 2초
// 3초
// 부승관
// 입력한 값 : 부승관3초에 카운트 종료
}
}
<Part04_Synchronized>
1. Thread
1) Consumer
package com.kh.part04_synchronized.thread;
import com.kh.part04_synchronized.model.vo.Buffer;
// 공유 자원을 꺼내서 사용하는 소비자 클래스
public class Consumer extends Thread{
private Buffer criticalData;
public Consumer(Buffer buffer){
this.criticalData = buffer;
}
@Override
public void run(){
for(int k = 1; k <= 10; k++){
criticalData.getData();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2) Producer
package com.kh.part04_synchronized.thread;
import com.kh.part04_synchronized.model.vo.Buffer;
// 공유 데이터 값 넣는 일을 하는 공급자(생산자)
public class Producer extends Thread{
private Buffer criticalData;
public Producer(Buffer buffer){
this.criticalData = buffer;
}
@Override
public void run(){
for (int k = 1; k <= 10; k++) {
criticalData.setData(k);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2. Buffer
package com.kh.part04_synchronized.model.vo;
// 공유 자원을 제공하는 클래스
public class Buffer {
private int data;
private boolean empty = true;
/* 해당 메소드에 동기화 처리 */
public synchronized void getData(){
// data 필드에 값이 없는 동안은 대기 상태
while(empty){
System.out.println(Thread.currentThread().getName() + " is waiting...");
try {
// 데이터가 없는 동안 계속해서 한 스레드가 락을 보유한 채로 있지 않도록
// wait()를 호출하여 스레드가 락을 반납하고 기다리게 함
// notify()가 호출 될 때까지 기다림
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 값이 있으면 리턴하고, 다시 empty는 true로 바꿈
empty = true;
System.out.println(Thread.currentThread().getName()
+ " : " + data + "번 물품을 소비하였습니다.");
notify(); // 대기 상태의 스레드를 실행 상태로 바꿈
}
/* 해당 메소드에 동기화 처리 */
public synchronized void setData(int data){
// 현재 값이 있으면 대기 상태로, 값이 없으면 값 기록 처리
while(!empty){
System.out.println(Thread.currentThread().getName() + " is waiting...");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 값을 기록하고 empty는 false로 바꿈
this.data = data;
empty = false;
System.out.println(Thread.currentThread().getName()
+ " : " + data + "번 물품을 생산하였습니다.");
notify(); // 대기 상태의 스레드를 실행 상태로 바꿈
}
}
3. Run
package com.kh.part04_synchronized.run;
import com.kh.part04_synchronized.model.vo.Buffer;
import com.kh.part04_synchronized.thread.Consumer;
import com.kh.part04_synchronized.thread.Producer;
public class TestProducerConsumer {
// 생산자-소비자 스레드 테스트
public static void main(String[] args) {
Buffer buffer = new Buffer();
Thread t1 = new Producer(buffer);
Thread t2 = new Consumer(buffer);
//이름 지정
t1.setName("Producer");
t2.setName("Consumer");
t1.start();
t2.start();
}
}
'웹개발 수업 > JAVA' 카테고리의 다른 글
[Day +32]미니 프로젝트 4일차 (0) | 2021.08.06 |
---|---|
[Day +31]네트워크 & 미니 프로젝트 3일차 (0) | 2021.08.05 |
[Day +29]이벤트 & 미니 프로젝트 1일차 (0) | 2021.08.04 |
[Day +28]JavaSwing Test / CheckBox, ImageLabel, ComboBox, List, Spinner, Slider, Dialog (0) | 2021.07.31 |
[Day +27]GUI / Java Swing, 컨테이너, 컴포넌트 (0) | 2021.07.29 |