Thread_1

jaegeunsong97·2023년 1월 15일
0
post-thumbnail

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() 메소드 재정의
// 2개의 스레드 <<--
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){}
        }
    }
}

// 3개의 스레드 <<--
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객체를 생성자의 매개변수로 넘기기
// 2개의 스레드 <<--
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){}
        }
    }
}

// 3개의 스레드 <<--
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

profile
현재 블로그 : https://jasonsong97.tistory.com/

0개의 댓글