JavaRush /Java Blog /Random-KO /스레드로 Java를 망칠 수는 없습니다: 1부 - 스레드
Viacheslav
레벨 3

스레드로 Java를 망칠 수는 없습니다: 1부 - 스레드

Random-KO 그룹에 게시되었습니다

소개

멀티스레딩은 초기부터 Java에 내장되어 있습니다. 그럼 멀티스레딩이 무엇인지 간단히 살펴보겠습니다. 스레드로 Java를 망칠 수는 없습니다: 1부 - 스레드 - 1Oracle의 공식 강의를 출발점으로 삼아 보겠습니다. " Lesson: The "Hello World!" Application ". Hello World 애플리케이션의 코드를 다음과 같이 약간 변경해 보겠습니다.
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
args프로그램이 시작될 때 전달되는 입력 매개변수의 배열입니다. 이 코드를 클래스 이름과 확장자가 일치하는 이름의 파일에 저장해 보겠습니다 .java. javac 유틸리티를 사용하여 컴파일해 보겠습니다 . javac HelloWorldApp.java 그런 다음 Roger와 같은 일부 매개변수를 사용하여 코드를 호출합니다. java HelloWorldApp Roger 스레드로 Java를 망칠 수는 없습니다: 1부 - 스레드 - 2이제 코드에 심각한 결함이 있습니다. 인수를 전달하지 않으면(즉, java HelloWorldApp을 실행하면) 오류가 발생합니다.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
이름이 스레드에서 예외(예: 오류)가 발생했습니다 main. Java에는 일종의 스레드가 있다는 것이 밝혀졌습니다. 이것이 우리의 여행이 시작되는 곳입니다.

자바와 스레드

스레드가 무엇인지 이해하려면 Java 애플리케이션이 시작되는 방법을 이해해야 합니다. 코드를 다음과 같이 변경해 보겠습니다.
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			//Do nothing
		}
	}
}
이제 javac를 사용하여 다시 컴파일해 보겠습니다. 다음으로 편의상 별도의 창에서 Java 코드를 실행하겠습니다. Windows에서는 다음과 같이 할 수 있습니다: start java HelloWorldApp. 이제 jps 유틸리티를 사용하여 Java가 우리에게 알려주는 정보를 살펴보겠습니다. 스레드로 Java를 망칠 수는 없습니다: 1부 - 스레드 - 3첫 번째 숫자는 프로세스 식별자인 PID 또는 프로세스 ID입니다. 프로세스란 무엇입니까?
Процесс — это совокупность codeа и данных, разделяющих общее виртуальное addressное пространство.
프로세스의 도움으로 서로 다른 프로그램의 실행이 서로 격리됩니다. 각 응용 프로그램은 다른 프로그램을 방해하지 않고 자체 메모리 영역을 사용합니다. " https://habr.com/post/164487/ " 기사를 더 자세히 읽어 보시기 바랍니다 . 스레드 없이는 프로세스가 존재할 수 없으므로 프로세스가 존재하면 적어도 하나의 스레드가 존재합니다. Java에서는 어떻게 이런 일이 발생합니까? Java 프로그램을 실행할 때 해당 실행은 main. 우리는 일종의 프로그램에 들어가기 때문에 이 특별한 방법을 main진입점 또는 "진입점"이라고 부릅니다. 메소드는 항상 JVM(Java Virtual Machine)이 프로그램 실행을 시작할 수 있도록 main해야 합니다 . 자세한 내용은 public static void" Java 기본 메소드가 왜 정적인가요? "를 참조하세요. Java 실행 프로그램(java.exe 또는 javaw.exe)은 간단한 애플리케이션(간단한 C 애플리케이션)이라는 것이 밝혀졌습니다. 이는 실제로 JVM인 다양한 DLL을 로드합니다. Java 실행 프로그램은 특정 JNI(Java Native Interface) 호출 세트를 만듭니다. JNI는 Java Virtual Machine의 세계와 C++의 세계를 연결하는 메커니즘입니다. 런처는 JVM이 아니라 로더라는 것이 밝혀졌습니다. JVM을 시작하기 위해 실행할 올바른 명령을 알고 있습니다. JNI 호출을 사용하여 필요한 모든 환경을 구성하는 방법을 알고 있습니다. 이러한 환경 구성에는 일반적으로 이라고 하는 메인 스레드 생성도 포함됩니다 main. Java 프로세스에 어떤 스레드가 있는지 더 명확하게 확인하기 위해 JDK에 포함된 jvisualvm 프로그램을 사용합니다. 프로세스의 pid를 알면 즉시 데이터를 열 수 있습니다. jvisualvm --openpid айдипроцесса 스레드로 Java를 망칠 수는 없습니다: 1부 - 스레드 - 4흥미롭게도 각 스레드에는 프로세스에 할당된 메모리에 별도의 영역이 있습니다. 이 메모리 구조를 스택이라고 합니다. 스택은 프레임으로 구성됩니다. 프레임은 메소드를 호출하는 지점, 실행 지점입니다. 프레임은 StackTraceElement로 표시될 수도 있습니다( StackTraceElement 용 Java API 참조 ). 각 스레드에 할당된 메모리에 대한 자세한 내용은 여기에서 확인할 수 있습니다 . Java API를 보고 Thread라는 단어를 검색하면 java.lang.Thread 클래스가 있음을 알 수 있습니다 . Java에서 스트림을 나타내는 것은 이 클래스이며 우리가 작업해야 하는 것은 바로 이 클래스입니다. Thread'ом Java не испортишь: Часть I — потоки - 5

java.lang.스레드

Java의 스레드는 클래스의 인스턴스로 표시됩니다 java.lang.Thread. Java의 Thread 클래스 인스턴스가 스레드 자체가 아니라는 점을 즉시 이해하는 것이 좋습니다. 이는 JVM과 운영 체제에서 관리하는 하위 수준 스레드를 위한 일종의 API입니다. Java 실행 프로그램을 사용하여 JVM을 시작하면 이름 main과 몇 가지 추가 서비스 스레드가 있는 기본 스레드가 생성됩니다. Thread 클래스의 JavaDoc에 명시된 바와 같이 When a Java Virtual Machine starts up, there is usually a single non-daemon thread 스레드에는 데몬과 비데몬이라는 두 가지 유형이 있습니다. 데몬 스레드는 백그라운드에서 일부 작업을 수행하는 백그라운드 스레드(서비스 스레드)입니다. 이 흥미로운 용어는 "Maxwell's Demon"에 대한 참조이며, " 악마 "에 대한 Wikipedia 기사에서 더 자세히 읽을 수 있습니다. 문서에 명시된 대로 JVM은 다음이 될 때까지 프로그램(프로세스)을 계속 실행합니다.
  • Runtime.exit 메소드가 호출되지 않았습니다.
  • 데몬이 아닌 모든 스레드가 작업을 완료했습니다(오류 및 예외 발생 없음).
따라서 중요한 세부 사항은 실행 중인 모든 명령에서 데몬 스레드가 종료될 수 있다는 것입니다. 따라서 해당 데이터의 무결성이 보장되지 않습니다. 따라서 데몬 스레드는 일부 서비스 작업에 적합합니다. 예를 들어, Java에는 GC(가비지 수집기)와 관련된 finalize 메서드 또는 스레드 처리를 담당하는 스레드가 있습니다. 각 스레드는 일부 그룹( ThreadGroup ) 에 속합니다 . 그리고 그룹은 서로 결합하여 계층 구조나 구조를 형성할 수 있습니다.
public static void main(String []args){
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
그룹을 사용하면 흐름 관리를 간소화하고 추적할 수 있습니다. 그룹 외에도 스레드에는 자체 예외 처리기가 있습니다. 예를 살펴보겠습니다:
public static void main(String []args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("An error occurred: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
0으로 나누면 처리기가 포착하는 오류가 발생합니다. 핸들러를 직접 지정하지 않으면 기본 핸들러 구현이 작동하여 StdError에 오류 스택이 표시됩니다. 리뷰 http://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/ "에서 자세한 내용을 읽을 수 있습니다. 또한 스레드에는 우선 순위가 있습니다. 우선 순위에 대한 자세한 내용은 다음에서 읽을 수 있습니다. 기사 " 멀티스레딩의 Java 스레드 우선순위 ".

스레드 만들기

문서에 명시된 대로 스레드를 생성하는 방법에는 두 가지가 있습니다. 첫 번째는 자신의 상속인을 만드는 것입니다. 예를 들어:
public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");
        }
    }

    public static void main(String []args){
        Thread thread = new MyThread();
        thread.start();
    }
}
보시다시피 작업은 메소드에서 시작되고 run스레드는 메소드에서 시작됩니다 start. 혼동하지 마십시오. 왜냐하면... 메소드를 run직접 실행하면 새 스레드가 시작되지 않습니다. startJVM에게 새로운 스레드 생성을 요청하는 메소드입니다 . Thread의 자손이 있는 옵션은 클래스 계층 구조에 Thread를 포함하기 때문에 좋지 않습니다. 두 번째 단점은 "단독 책임" SOLID 원칙을 위반하기 시작했다는 것입니다. 우리 클래스는 스레드 관리와 이 스레드에서 수행되어야 하는 일부 작업을 동시에 담당하게 됩니다. 어느 것이 맞나요? 대답은 run우리가 재정의하는 바로 그 메서드에 있습니다.
public void run() {
	if (target != null) {
		target.run();
	}
}
다음은 클래스의 인스턴스를 생성할 때 Thread에 전달할 수 있는 target일부 입니다 . java.lang.Runnable따라서 우리는 다음과 같이 할 수 있습니다.
public class HelloWorld{
    public static void main(String []args){
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
또한 RunnableJava 1.8 이후의 기능적 인터페이스이기도 합니다. 이를 통해 스레드에 대한 작업 코드를 더욱 아름답게 작성할 수 있습니다.
public static void main(String []args){
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

그래서 저는 이 이야기를 통해 스트림이 무엇인지, 어떻게 존재하는지, 스트림을 통해 어떤 기본 작업을 수행할 수 있는지 명확해지기를 바랍니다. 다음 부분 에서는 스레드가 서로 상호 작용하는 방식과 수명 주기가 무엇인지 이해하는 것이 좋습니다. #비아체슬라프
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION