2023_1_15_TIL
프로그램과 프로세스의 개념
- 컴퓨터 구성요소 -> CPU, MEMORY, HARD DISK
- CPU -> 연산 수행, 실제 프로그램 실행 -> 가장 빠름
- HARD DISK -> 데이터 저장역할 -> 가장 느림
- 하드디스크에 저장된 프로그램 실행 -> 프로그램을 메모리로 로딩하는 과정 필요함 -> 프로세스 상태 -> 로딩된 메모리의 프로세스 상태(속도) == CPU(속도) -> 프로그램 실행 성공!
- 프로그램 -> 하드디스크에 저장된 파일들의 모임
- 프로세스 -> 메모리상에 로딩된 프로그램 -> 필요한 부분만 동적 로딩
- 멀티 프로세스
- 메모리에 2번 로딩 -> 2개의 프로세스 동작(요런 느낌)
쓰레드의 개념
- CPU는 메모리와만 대화 = CPU는 메모리내의 프로세스와 대화 = CPU는 메모리내의 프로세스내의 쓰레드와만 대화 = 쓰레드는 CPU를 사용하는 최소 단위(핵심)

자바 프로그램에서의 쓰레드
- 프로그램이 처음 실행 -> main쓰레드 1개만 존재
- 만약 2개의 쓰레드 생성 후 실행 -> 멀티 쓰레드 프로세스
멀티 쓰레드의 중요성
- 아래와 같이 작성하면 모든게 순차적으로 실행됨 -> 원하는 것은 동시에 나오는 것
- 해결방법은 '멀티스레드'
int[] intArray = {1, 2, 3, 4, 5};
String[] strArray = {"하나", "둘", "셋", "넷", "다섯"};
for (int i = 0; i < intArray.length; i++) {
System.out.println("(비디오 프레임) " + intArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
for (int i = 0; i < strArray.length; i++) {
System.out.println("(자막 번호) " + strArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
쓰레드는 정말 동시에 수행될까?
- 어떻게 쓰레드는 동시에 실행되는 지 -> 동시성(concurrency)와 병렬성(parallelism)
- 단일 스레드로 2개의 작업 처리 -> 각 작업은 순차적(sequential)으로 처리
- 멀티 스레드 -> 동시성 or 병렬성
- 동시성
- CPU 개수(1) < 동시에 처리 작업 개수(2)
- 짧은 간격으로 교차 실행 -> 동시에 하는 것처럼 보이게 하는 것(핵심)
- 병렬성
- CPU 개수(2) < 동시에 처리 작업 개수(1)
- 각각의 작업을 코어에 할당(동시 작업)
- 멀티 스레드의 목적
- 병렬성과 동시성을 활용해서 작업들을 동시에 실행 or 동시에 진행하는 것처럼 보이게 하는 것
쓰레드 생성 및 실행 방법
- 스레드 실행방법 -> start() 호출
- 한 번 사용된 스레드 재사용 X -> 다시 하고싶다? -> 객체 다시 생성
- 스레드는 CPU의 최소 단위 -> CPU와 대화할려면 준비할 것이 많음(스택 메모리 생성 포함)
- start() -> 새로운 스레드생성/추가하기 이한 모든 준비 + 새로운 스레드 위에 run()실행
- 멀티 스레드는 독립적으로 실행
- start()로 먼저 호출되도 나중에 실행된 스레드보다 늦게 실행할 수도 있음
- 방법 1.Tread클래스 상속받아 run() 메소드 재정의
class SMIFileThread extends Thread {
@Override
public void run() {
String[] strArray = {"하나", "둘", "셋", "넷", "다섯"};
try {Thread.sleep(10);} catch (InterruptedException e){}
for (int i = 0; i < strArray.length; i++) {
System.out.println(" - (자막 번호) " + strArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
}
public class CreateAndStartThread_M1C1 {
public static void main(String[] args) {
Thread smiFileThread = new SMIFileThread();
smiFileThread.start();
int[] intArray = {1, 2, 3, 4, 5};
for (int i = 0; i < intArray.length; i++) {
System.out.print("(비디오 프레임) " + intArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
}
class SMIThread extends Thread {
@Override
public void run() {
String[] strArray = {"하나", "둘", "셋", "넷", "다섯"};
try {Thread.sleep(10);} catch (InterruptedException e){}
for (int i = 0; i < strArray.length; i++) {
System.out.println(" - (자막 번호) " + strArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
}
class VideoFileThread extends Thread {
@Override
public void run() {
int[] intArray = {1, 2, 3, 4, 5};
for (int i = 0; i < intArray.length; i++) {
System.out.print("(비디오 프레임) " + intArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
}
public class CreateAndStartThread_M1C2 {
public static void main(String[] args) {
Thread smiFileThread = new SMIThread();
smiFileThread.start();
Thread vedioFileThread = new VideoFileThread();
vedioFileThread.start();
}
}
- 방법 2.Runnable인터페이스 구현 객체를 생성한 후 Thread생성자로 Runnable객체 전달
- Runnable인터페이스는 run()를 추상메소드로 가짐
- Runnable인터페이스는 start()가 없음 -> start()가진 Thread객체 생성 필요
- Thread객체를 생성할 때 Runnable객체를 생성자의 매개변수로 넘기기
class SMIFileThread implements Runnable {
@Override
public void run() {
String[] strArray = {"하나", "둘", "셋", "넷", "다섯"};
try {Thread.sleep(10);} catch (InterruptedException e){}
for (int i = 0; i < strArray.length; i++) {
System.out.println(" - (자막 번호) " + strArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
}
public class CreateAndStartThread_M2C1 {
public static void main(String[] args) {
Runnable smiFileThread = new SMIFileThread();
Thread thread = new Thread(smiFileThread);
thread.start();
int[] intArray = {1, 2, 3, 4, 5};
for (int i = 0; i < intArray.length; i++) {
System.out.print("(비디오 프레임) " + intArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
}
class SMIFileThread implements Runnable {
@Override
public void run() {
String[] strArray = {"하나", "둘", "셋", "넷", "다섯"};
try {Thread.sleep(10);} catch (InterruptedException e){}
for (int i = 0; i < strArray.length; i++) {
System.out.println(" - (자막 번호) " + strArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
}
class VideoFileThread implements Runnable {
@Override
public void run() {
int[] intArray = {1, 2, 3, 4, 5};
for (int i = 0; i < intArray.length; i++) {
System.out.print("(비디오 프레임) " + intArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
}
public class CreateAndStartThread_M2C2 {
public static void main(String[] args) {
Runnable smiFileThread = new SMIFileThread();
Thread thread1 = new Thread(smiFileThread);
thread1.start();
Runnable videoFileThread = new VideoFileThread();
Thread thread2 = new Thread(videoFileThread);
thread2.start();
}
}
public class CreateAndStartThread_M2C3 {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
String[] strArray = {"하나", "둘", "셋", "넷", "다섯"};
try {Thread.sleep(10);} catch (InterruptedException e){}
for (int i = 0; i < strArray.length; i++) {
System.out.println(" - (자막 번호) " + strArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
int[] intArray = {1, 2, 3, 4, 5};
for (int i = 0; i < intArray.length; i++) {
System.out.print("(비디오 프레임) " + intArray[i]);
try {Thread.sleep(200);} catch (InterruptedException e){}
}
}
});
thread1.start();
thread2.start();
}
}
현재 쓰레드 객체 잠좃값 얻어오기
- 스레드 객체를 참조할 수 없을 때만
- Thread클래스의 정적메소드 static Thread Thread.currentThread() 사용
실행 중인 쓰레드의 개수 가져오기
- 여러 개의 스레드 실행 중, 현재 실행중인 스레드의 개수
- Thread클래스의 정적메소드 static int Thread.activeCount() 사용
- 동일한 그룹내에서 실행 중인 스레드의 개수
- main스레드에서 생성한 스레드는 모두 같은 main스레드
쓰레드의 이름 지정 및 가져오기
- Thread클래스의 인스턴스 메소드 String setName(String name) 사용
- 이름 지정안하면 컴파일러가 알아서 자동으로 부여
- 인스턴스 메소드이기 때문에 객체 생성 후 부여
- Thread클래스의 인스턴스 메소드 String getName() 사용
Thread curThread = Thread.currentThread();
System.out.println("현재 쓰레드의 이름 = " + curThread.getName());
System.out.println("동작하는 쓰레드의 개수 = " + curThread.activeCount());
for (int i = 0; i < 3; i++) {
Thread thread = new Thread();
System.out.println(thread.getName());
thread.start();
}
for (int i = 0; i < 3; i++) {
Thread thread = new Thread();
thread.setName(i + "번째 쓰레드");
System.out.println(thread.getName());
thread.start();
}
for (int i = 0; i < 3; i++) {
Thread thread = new Thread();
System.out.println(thread.getName());
thread.start();
}
System.out.println("동작하는 쓰레드의 개수 = " + Thread.currentThread());
쓰레드의 우선순위
- 1은 가장 낮은, 10은 가장 높은
- 우선순위는 스레드의 동시성과 관계 -> 우선순위가 높으면 더 많은 시간 할당
- Thread클래스의 인스턴스 메소드
- void setPriority(int priority)
- int getPriority()
- 현재 컴퓨터의 CPU코어수
- public native int availableProcessors()
- CPU가 하이퍼스레드다? -> 실제 코어 수의 2배를 리턴함
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000000000; i++) {}
System.out.println(getName() + " 우선순위: " + getPriority());
}
}
public class ThreadProperties_2 {
public static void main(String[] args) {
System.out.println("코어수: " + Runtime.getRuntime().availableProcessors());
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
try {Thread.sleep(1000);} catch (InterruptedException e){}
for (int i = 0; i < 10; i++) {
Thread thread = new MyThread();
thread.setName(i + "번쨰 쓰레드");
if (i == 9) thread.setPriority(10);
thread.start();
}
}
}
- 스레드는 실행되기전에 일정 시간의 준비과정(메모리 할당 등)이 필요
- for문을 집어넣는 이유
- 이렇게 안하면 우선순위가 높아도 뒤로 밀릴수 있음
쓰레드의 데몬 설정
- 데몬스레드 -> 일반 스레드가 모두 종료되면 함께 종료되는 스레드(핵심)
- Thread클래스의 인스턴스 메소드
- void setDaemon(boolean on)
- boolean isDaemon()
- 데몬스레드 설정 -> start() 메소드 호출 전에, 일반 스레드 실행 후 데몬 설정 못 바꿈
class MyThread extends Thread {
@Override
public void run() {
System.out.println(getName() + ": " + (isDaemon() ? "데몬스레드" : "일반스레드"));
for (int i = 0; i < 6; i++) {
System.out.println(getName() + ": " + i + "초");
try {Thread.sleep(1000);} catch (InterruptedException e){}
}
}
}
public class ThreadProperties_3 {
public static void main(String[] args) {
Thread thread1 = new MyThread();
thread1.setDaemon(false);
thread1.setName("thread1");
thread1.start();
try {Thread.sleep(3500);} catch (InterruptedException e){}
System.out.println("main Thread 종료");
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println(getName() + ": " + (isDaemon() ? "데몬스레드" : "일반스레드"));
for (int i = 0; i < 6; i++) {
System.out.println(getName() + ": " + i + "초");
try {Thread.sleep(1000);} catch (InterruptedException e){}
}
}
}
public class ThreadProperties_3_2 {
public static void main(String[] args) {
Thread thread2 = new MyThread();
thread2.setDaemon(true);
thread2.setName("thread2");
thread2.start();
try {Thread.sleep(3500);} catch (InterruptedException e){}
System.out.println("main Thread 종료");
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println(getName() + ": " + (isDaemon() ? "데몬스레드" : "일반스레드"));
for (int i = 0; i < 6; i++) {
System.out.println(getName() + ": " + i + "초");
try {Thread.sleep(1000);} catch (InterruptedException e){}
}
}
}
public class ThreadProperties_3_3 {
public static void main(String[] args) {
Thread thread1 = new MyThread();
thread1.setDaemon(false);
thread1.setName("thread1");
thread1.start();
Thread thread2 = new MyThread();
thread2.setDaemon(true);
thread2.setName("thread2");
thread2.start();
try {Thread.sleep(3500);} catch (InterruptedException e){}
System.out.println("main Thread 종료");
}
}
참조
https://velog.io/@raejoonee/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98-%EC%B0%A8%EC%9D%B4