스레드

(*교재 p.687 참고)

일을 처리하는 실행의 단위

어떠한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의(일의 실행의) 단위를 말한다.

스레드의 핵심은 타임스레싱이다. = RR(라운드로빈)​​

자바는 메인 스레드를 가지고 있다. 일반적으로 한 프로그램은 하나의 스레드를 가진다.

벽을 나누고 각자 다른색을 동시에 칠한다고하면 빠른시간에 여러가지일을 동시에 시작하고 끝낼 수 있다

-> 멀티 프로세싱 : 프로세스가 세개 떠서 동시에 일을 처리하는 것을 말한다.

ex. 메인코드가 실행됐을 때 while문을 계속 돌리다가 while문이 끝나면 종료되는 것을 세개 동시에 시작하는 것

프로그램1 ,프로그램2, 프로그램 3를 동시에 처리한다면 : 멀티 프로세싱

멀티 스레드

일반적으로 한 프로그램은 하나의 스레드를 가지고 있지만, 프로그램 환경에 따라 둘 이상의 스레드를 동시에 실행할 수 있다.

이러한 실행방식을 멀티 스레드라고 한다.

* 동시에 실행할 수 없으면 멀티스레드 아님!

target : 스레드에서 스택을 들고 있는 것

* Target의 조건

1. Runnable 타입

2. run() 스택이 필요하다.

운영체제한테 스레드를 달라고 하려면 stack이 있어야 한다.

stack을 들고있는 Target 을 만드려면 class를 가지고 있어야한다

-> Runnable 타입을 지녀야한다.

 

Thread의 활용

 

① Thread 클래스를 상속받은 후, run() 메소드를 원하는 작업을 하도록 오버라이딩

②완성된 클래스를 생성한 후, start() 메소드 실행

앱이 있다고 가정, html영역은 빠른 속도로 화면에 나타나지만

BufferedReader로 읽어들이는 사진 자료는 출력 속도가 다른 부분에 비해 느리다면

사용자의 눈에는 사진 자료가 안뜨고 작업이 진행되지 않는다. 즉 UX 화면이 보기 안좋음

CPU가 어떤 데이터를 처리하려면 RAM에 존재해야함

RAM에 자료 저장 --> CPU가 연산해서 출력 --> 하드디스크에 기록

CPU가 대기상태로 있을 때, 다른 스레드에게 작업을 넘김

(사진자료가 아직 다운 받아지지 않았기 때문에, CPU는 대기상태)

사진자료 다운받을 동안 밑의 다른 작업을 처리한다. 다운이 완료되면 다시 사진자료로 올라와서 작업을 진행한다.

이 때는 밑의 작업과 사진 자료 작업을 동시에 진행된다.

 

https://gmlwjd9405.github.io/2018/09/14/process-vs-thread.html

 

[OS] 프로세스와 스레드의 차이 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

 

스레드는 OS가 가지고 있다.

OS가 가지고 있는 기능을 불러와서 사용하는 것을 시스템콜이라고 한다.

OS는 Runnable 타입의 heap 공간에 run 메서드를 호출해야 된다는 걸 이미 알고 있다.

스레드를 쓸 때 OS에게 요청 --> 어떤 스택을 호출해주면 되냐고 물어봄 --> Runnable 타입인 run()로 실행할게

(시스템 콜)

new Thread

 

start() 메소드 호출 --> Thread 생성 --> run() 메서드 실행 --> run() 메서드에서 실제 Thread가 실행할 모든 일 정의

--> run() 메서드가 종료되면 Thread는 자동 소멸된다.

Runnable

 

① Runnable 인터페이스를 상속받은 후, run() 메서드를 원하는 작업을 하도록 구현

② 완성된 클래스를 생성, Thread 클래스의 생성자에게 인수로 전달

https://opentutorials.org/module/1226/8027

 

65. 스레드 생성방법을 이해하고 사용할수 있다. ( new Thread, Runnable ) - level up 과정 [JAVA]

Thread 생성방법 이해 1. new Thread ① Thread 클래스를 상속받은 후, run() 메소드를 원하는 작업을 하도록 오버라이딩 ② 완성된 클래스를 생성한 후, start() 메소드를 실행 public class MyThread extends Thread { public void run() { // 실행할 명령들 while(true) // 무한루프 { System.out.println("쓰레드가 실행 중입니다."); } } } ... MyT

opentutorials.org

문맥교환(Context Switch)

컴퓨터가 동시에 처리할 수 있는 최대 작업 수는 CPU의 코어 수와 같다.

만약 CPU의 코어 수보다 더 많은 스레드가 실행되면, 각 코어가 정해진 시간 동안 여러 작업을 번갈아가며 수행하게 된다. 이때 각 스레드가 서로 교체될 때 스레드 간의 문맥 교환이 발생한다.

현재까지의 작업 상태나 다음 작업에 필요한 각종 데이터를 저장하고 읽어오는 작업을 가리킨다.

문맥 교환에 걸리는 시간이 커지면 커질수록, 멀티스레딩의 효율이 저하된다.

*하드디스크가 일을 처리하려면 데이터가 RAM에 있어야 한다.

다운받은 것을 RAM이 저장하고 CPU가 연산작용한다. 그 후 RAM이 하드디스크에 저장한다.

RAM이 저장할때 CPU가 노는 시간을 없애면 속도가 빨라진다.(이때 스레드를 사용하면 속도가 빨라진다는 말)

스레드가 하나면 보글보글게임에서 내 캐릭터는 움직이는데 죽여야 할 적은 안 움직이는 일이 발생한다. 움직이는 적의 수만큼 스레드가 필요하다.

I/O(저장장치)가 일어날때 스레드를 이용해서 속도를 빠르게 할 수 있다.

* 마샬링 : 객체의 메모리 구조를 저장이나 전송을 위해서 적당한 자료 형태로 변형하는 것을 말한다.

서로 다른 컴퓨터 혹은 서로 다른 프로그램간에 데이터가 이동되어야 할 경우 사용된다.

 

package ch13;

class Sub implements Runnable {
	@Override
	public void run() {
		for (int i = 1; i < 11; i++) {
			System.out.println("서브  스레드 : " + i);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

public class ThreadEx01 {

	// 메인 스레드
	public static void main(String[] args) {
		Thread t1 = new Thread(new Sub()); // new Sub : target
		t1.start();

		for (int i = 1; i < 6; i++) {
			System.out.println("메인 스레드 : " + i);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

 

비동기

해야될 일이 있을때

ex. 라면을 끓일 때

1. 냄비준비 2. 물을 채운다 3. 물을 끓인다 4. 라면과 스프를 넣는다 5. 라면을 끓인다 6. 완성

-> 알고리즘

* 동기적이다 : 일에 순서가 있다(순서가 일치하는 것). -> 프로그램에서의 동기화

데이터베이스에서는 데이터의 일치를 동기화라고도 한다.

해야될 일이 두가지가 있을때 동시에 하는 것을 스레드라고 함

동시에 하면서 마지막에 콜백을 해야한다.

이 콜백의 타이밍 찾기가 힘들다.

* 비동기는 스레드만 돌리면 됨 -> 비동기 처리를 잘 해야한다.

비동기 : 일의 순서가 왔다갔다 함

토요일에 약속이 있는경우 월~금동안 자유를 가지지만 토요일 약속이 확실하지 않다.

중간에 다른일이 끝나지 못 하면 끝날때 까지 원칙적으로 못 가지만 비동기처리로 11시에 콜백해서 약속을 이행한 후 다시 이전의 일을 끝내는 것

 

package ch13;

class DownloadThread implements Runnable {

	int data = 10000;

	@Override
	public void run() {
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		data = data + 20000;
		System.out.println("금액 다운로드 종료");
	}
}

public class ThreadEx02 {

	public static void main(String[] args) {
		System.out.println("프로그램 시작");
		System.out.println("--------");

		DownloadThread dt = new DownloadThread();
		Thread t1 = new Thread(dt);
		t1.start();

		System.out.println("금액은 : " + dt.data);
	}

}


 

 

* 규칙 : 추상메서드가 있기 때문에 추상적이라 object가 될 수 없다.

new 하기 위해서는 구현한 object가 있으면 띄울 수 있다.

* 이것 또한 추상 메서드가 구현되어 있지 않아서 new 할 수 없다.

추상클래스는 추상 메서드가 들어올 수 있기 때문에 new를 막아둔다

익명클래스

인터페이스를 구현하기 위해 해당 클래스를 생설할 때 일회성이고 재사용할 필요가 없다면 굳이 클래스 파일을 만들 필요가 없다.

이 경우 익명클래스를 사용하면 된다.

클래스의 선언과 객체의 생성을 동시에 한다.

* 익명객체는 단독생성이 불가능하다. 클래스를 상속하거나 인터페이스를 구현해야 한다.

익명객체에 새롭게 정의된 필드와 메소드는 익명 객체의 내부에서만 사용가능하다. 외부에서는 익명 객체에서 새롭게 생성한 필드와 메소드에 접근할 수 없다. 왜냐하면 익명 객체는 부모 타입 변수에 대입되기 때문에 부모에서 선언된 것만 사용가능하다.

 

package ch13;

interface Dog {
	void sound();
}

public class AnomyEx02 {
	
	static void start (Dog d) {
		d.sound();
	}

	public static void main(String[] args) {
		Dog d = new Dog() {
			
			@Override
			public void sound() {
				System.out.println("멍멍");
			}
		};
		start(d);
	}

}

 

 

클래스 이름 없는 클래스이다.

Do까지만 치고 ctrl + space : 익명으로 만들 수 있는 클래스를 만드는 창을 실행할 수 있다.

 

네모친 부분이 익명클래스이다

이렇게 만들면 테스트 클래스를 안 만들어도 되는 장점이 있다.

package ch13;

class Car {
	void run () {
		System.out.println("자동차는 달린다.");
	}
}

class Sonata extends Car {
	@Override
	void run() {
		super.run();
	}
}

class Granger extends Car {
	@Override
	void run() {
		super.run();
	}
}

public class AnomyEx03 {
	
	static void start (Car c) {
		c.run();
	}

	public static void main(String[] args) {
		start(new Car());
	}

}

 

 

 

이 경우는 익명클래스가 필요없다 -> 애초에 재정의를 할 필요가 없음 그렇기 때문에 override 한 부분을 지워도 된다.

이런데서 익명클래스 X

 

package ch13;

class Ani {
	void sound() {
		System.out.println("?");
		// 어떤동물이 어떻게 울지 모르니까 추상클래스임 
	}
}

class Cat extends Ani {
	@Override
	void sound() {
		System.out.println("야옹");
	}
}

class Bird extends Ani {
	@Override
	void sound() {
		System.out.println("짹짹");
	}
}

public class AnomyEx04 {

	public static void main(String[] args) {
		
	}

}

 

 

어짜피 밑에 동물이 어떻게 울지 모르기때문에 이렇게 만들 필요가 없음

 

package ch13;

abstract class Ani {
	abstract void sound();
}

class Cat extends Ani {
	@Override
	void sound() {
		System.out.println("야옹");
	}
}

class Bird extends Ani {
	@Override
	void sound() {
		System.out.println("짹짹");
	}
}

class Fish extends Ani {
	void sound() {
		System.out.println("뻐끔뻐끔");
	}
}

public class AnomyEx04 {
	
	static void start (Ani a) {
		a.sound();
	}

	public static void main(String[] args) {
		start(new Cat());
		start(new Fish());
	}

}

 

이렇게 수정한다.

다만, 이렇게 되면 동물을 추가할때마다 class 를 새로 만들어서 추가시켜줘야한다.

이렇게 수정해주면 매번 동물을 추가하지 않고 해당 void 안에 추가해주면 된다.

* 익명클레스는 구현체가 동적일때 사용한다

 

package ch13;

abstract class Ani {
	abstract void sound();
}

class Cat extends Ani {
	@Override
	void sound() {
		System.out.println("야옹");
	}
}

class Bird extends Ani {
	@Override
	void sound() {
		System.out.println("짹짹");
	}
}

class Fish extends Ani {
	void sound() {
		System.out.println("뻐끔뻐끔");
	}
}

public class AnomyEx04 {
	
	static void start (Ani a) {
		a.sound();
	}

	public static void main(String[] args) {
		start(new Cat());
		start(new Fish());
		start(new Ani() {
			
			@Override
			void sound() {
				System.out.println("음메");
			}
		});
	}

}

수정완료 !

openclose