이 시리즈는 숫자를 금으로 정제하는 과정을 기반으로하며 더 잘 배우기 위해 일련의 레코드가 만들어졌습니다. 이 기사는 주로 1을 소개합니다. 스레드 2. 스레드의 기본 작동 3. 데몬 스레드 4. 스레드 우선 순위 5. 기본 스레드 동기화 작업
1. 실이란 무엇입니까?
스레드는 프로세스 내에서 실행 장치입니다
프로세스에는 여러 스레드가 있습니다.
스레드는 프로세스 내에서 실행 장치입니다.
스레드를 사용하는 이유는 프로세스 전환이 매우 헤비급 작동이며 리소스를 소비하기 때문입니다. 여러 프로세스를 사용하는 경우 동시성 수는 그리 높지 않습니다. 스레드는 스케줄링 장치가 작고 가벼우므로 실은 동시 설계에서 더 널리 사용됩니다.
Java에서 스레드의 개념은 운영 시스템 수준 스레드의 개념과 유사합니다. 실제로 JVM은 Java의 스레드를 운영 체제의 스레드 영역에 매핑합니다.
2. 스레드의 기본 작동
2.1 스레드 상태 다이어그램
위의 그림은 자바의 스레드의 기본 작동을 보여줍니다.
스레드가 새로운 경우 스레드가 실제로 작동하지 않습니다. 단지 엔티티를 생성 하고이 인스턴스의 시작 방법을 호출 할 때 실제로 스레드가 시작됩니다. 시작 후, 그것은 달리기 가능한 상태에 도달합니다. 실행 가능하다는 것은 스레드의 자원 등이 준비되어 있고 실행될 수 있음을 의미하지만, 그것이 실행 상태에 있다는 것을 의미하지는 않습니다. 타임 슬라이스의 회전으로 인해 현재 스레드가 실행되지 않을 수 있습니다. 우리에게는 스레드가 실행 된 것으로 간주 될 수 있지만 실행되는지 여부는 실제로 물리적 CPU의 스케줄링에 따라 다릅니다. 스레드 작업이 실행되면 스레드가 종료 상태에 도달합니다.
때로는 스레드를 실행하는 동안 일부 잠금 장치 또는 객체 모니터가 적용되는 것이 불가피합니다. 검색 할 수 없으면 스레드가 차단, 매달리 및 차단 된 상태에 도달합니다. 이 스레드가 대기 방법을 호출하면 대기 상태에 있습니다. 대기 상태에 들어가는 스레드는 다른 스레드가 알릴 때까지 기다립니다. 알림이 이루어지면 대기 상태는 대기 상태에서 달리기 상태로 전환하여 계속 실행됩니다. 물론 대기 상태에는 두 가지 유형이 있습니다. 하나는 알림이 통지 될 때까지 무기한으로 기다려야합니다. 제한된 기간을 기다리고 있습니다. 예를 들어, 10 초 동안 기다리거나 통보되지 않은 경우 자동으로 런닝 가능한 상태로 전환됩니다.
2.2 새 스레드를 만듭니다
스레드 스레드 = 새 스레드 ();
thread.start ();
이것은 스레드를 열어줍니다.
주목해야 할 것은
스레드 스레드 = 새 스레드 ();
thread.run ();
실행 메소드를 직접 호출하여 새 스레드를 열 수 없습니다.
시작 방법은 실제로 새로운 운영 체제 스레드에서 실행 메소드를 호출합니다. 다시 말해, 시작 메소드 대신 실행 메소드를 직접 호출하면 새 스레드가 시작되지 않지만 현재 스레드 호출 실행에서 작업을 수행합니다.
스레드 스레드 = 새 스레드 ( "t1") {@override public void run () {// todo auto-auto-renated method.out.println (thread.currentthread (). getName ()); }}; thread.start (); 시작이 호출되면 출력은 t1thread stread = new Thread ( "t1") {@override public void run () {// todo auto-auto-a-auto-regenated method.out.println (thread.currentthread (). getName ()); }}; thread.run (); 실행되면 메인이 출력됩니다. (직접 실행하는 것은 실제로 일반 기능 호출 일 뿐이며 멀티 스레딩의 역할을 달성하지 못했습니다)
실행 방법을 구현하는 두 가지 방법이 있습니다
첫 번째 방법은 실행 방법을 직접 재정의하는 것입니다. 현재 코드에서 볼 수 있듯이 익명 클래스를 사용하여 가장 편리한 방법을 달성 할 수 있습니다.
스레드 스레드 = 새 스레드 ( "t1") {@override public void run () {// todo auto-auto-renated method.out.println (thread.currentthread (). getName ()); }}; 두 번째 방법
스레드 t1 = 새로운 스레드 (new createTheRdead3 ());
CreateThread3 ()는 실행 가능한 인터페이스를 구현합니다.
Zhang Xiaoxiang의 비디오에서 두 번째 방법은 객체 지향적이라고 말하면서 권장됩니다.
2.3 스레드 종료
Thread.stop ()는 권장되지 않습니다. 모든 모니터를 출시합니다
소스 코드에서 정지 메소드가 더 이상 사용되지 않았으며 그 이유는 Javadoc에서도 설명되어 있습니다.
그 이유는 정지 방법이 너무 "폭력적"이기 때문입니다. 스레드가 어디에서 실행하든 즉시 스레드를 삭제하지 않습니다.
쓰기 스레드가 잠금을 가져 오면 데이터 작성을 시작합니다. ID = 1을 작성한 후에는 중지되고 이름 = 1을 설정할 준비를 할 때 잠금이 해제됩니다. 읽기 스레드는 읽기 작업을위한 잠금을 얻습니다. ID 읽기는 1이고 이름은 여전히 0이므로 데이터 불일치가 발생합니다.
가장 중요한 것은 이런 종류의 오류가 예외를 던지지 않고 감지하기가 어렵다는 것입니다.
2.4 스레드 인터럽트
스레드를 방해하는 3 가지 방법이 있습니다
public void stread.interrupt () // 인터럽트 스레드
public boolean thread.isinterrupted () // 중단되었는지 여부를 결정합니다
공개 정적 부울 스레드.
스레드 인터럽트 란 무엇입니까?
Java의 인터럽트 메커니즘을 이해하지 못한다면, 그러한 설명은 오해를 일으킬 가능성이 높으며, 스레드의 인터럽트 방법을 호출하면 스레드가 중단 될 것이라고 생각합니다.
실제로 Java Interruption은 협업 메커니즘입니다. 다시 말해, 스레드 객체의 인터럽트 메소드를 호출한다고해서 반드시 실행 스레드를 방해하는 것은 아니며 스레드가 적시에 스스로 방해해야합니다. 각 스레드에는 부울 인터럽트 상태가 있습니다 (실제로 객체의 속성은 아닙니다. 실제로이 상태는 실제로 스레드 필드가 아닙니다). 인터럽트 메소드는 단순히 상태를 true로 설정합니다. 비 블로킹 스레드의 경우 인터럽트 상태가 변경됩니다.
public void run () {// 스레드 t1 while (true) {thread.yield (); }} t1.interrupt (); 스레드 T1을 인터럽트하는 데 아무런 영향을 미치지 않지만 인터럽트 상태 비트가 변경됩니다.
이 스레드를 매우 우아하게 종료하려면이 작업을 수행해야합니다.
public void run () {while (true) {if (thread.currentThread (). islerrupted ()) {system.out.println ( "interruted!"); 부서지다; } thread.yield (); }}인터럽트를 사용하면 데이터 일관성에 대한 특정 보증이 제공됩니다.
이러한 함수, thread.sleep (), object.wait () 및 thread.join ()과 같은 취소 가능한 차단 상태의 스레드의 경우이 스레드는 인터럽트 신호를 수신 한 후 인터럽트 꺼짐을 던지고 인터럽트 상태를 다시 거짓으로 설정합니다.
차단 해제 상태의 스레드의 경우 다음과 같은 코드를 쓸 수 있습니다.
public void run () {while (true) {if (thread.currentThread (). islerrupted ()) {system.out.println ( "interruted!"); 부서지다; } try {thread.sleep (2000); } catch (InterruptedException e) {System.out.println ( "수면시 간호"); // 인터럽트 상태를 설정합니다. 예외 스레드를 던진 후 인터럽트 플래그 비트가 지워집니다 .CurrentThread (). 인터럽트 (); } thread.yield (); }}2.5 스레드가 매달려 있습니다
스레드를 중단하고 재개합니다
SUSTEND ()는 잠금을 해제하지 않습니다
이력서 () 전에 잠금이 발생하면 교착 상태 발생 방법 모두 더 이상 사용되지 않은 방법이며 권장되지 않습니다.
그 이유는 일시 중단이 잠금을 해제하지 않기 때문에 다른 스레드에 의해 재개 될 때까지 고정 된 임계 영역 자원에 액세스 할 수 없기 때문입니다. 실행중인 스레드 시퀀스는 제어 될 수 없기 때문에 다른 스레드의 이력서 메소드가 먼저 실행되면 나중에 일시 중단 된 나중에 자물쇠가 점유되어 교착 상태가 발생합니다.
다음 코드를 사용 하여이 시나리오를 시뮬레이션하십시오
패키지 테스트; 공개 클래스 테스트 {static 객체 u = new Object (); 정적 testsuspendthread t1 = new testsuspendthread ( "t1"); 정적 testsuspendthread t2 = new testsuspendthread ( "t2"); public static class testsuspendthread는 스레드 {public testsuspendthread (문자열 이름) {setName (name); } @override public void run () {synchronized (u) {system.out.println ( "in" + getName ()); thread.currentThread (). sultend (); }} public static void main (string [] args)은 인터럽트 exception {t1.start (); Thread.sleep (100); t2.start (); t1.resume (); t2.resume (); t1.join (); t2.join (); }} T1과 T2가 동시에 잠금을 위해 경쟁하고 경쟁하는 스레드가 매달린 다음 재개됩니다. 논리적으로 말하면, 경쟁 후 이력서에 의해 스레드가 해제되고 다른 스레드는 잠금 장치와 재개를 위해 경쟁해야합니다.
결과 출력은 다음과 같습니다.
T1에서
T2에서
이는 두 스레드가 잠금 장치와 경쟁하지만 콘솔의 빨간색 표시등이 여전히 켜져 있음을 의미합니다. 즉, T1과 T2에서 실행되지 않은 스레드가 있어야합니다. 그것을 버리고 살펴 보겠습니다
T2가 중단 된 것으로 밝혀졌습니다. 이것은 교착 상태를 만듭니다.
2.6 가입하고 예의
Yeild는 기본 정적 방법입니다. 이 방법은 소유 한 CPU 시간을 릴리스 한 다음 다른 스레드와 경쟁하는 것입니다 (Yeild의 스레드는 여전히 CPU와 경쟁 할 수 있으며 수면의 차이에주의를 기울일 수 있습니다). Javadoc에서 Yeild는 기본적으로 사용되지 않으며 일반적으로 디버그 및 테스트에 사용되는 방법이라고 설명합니다.
결합 메소드는 일시 중단 섹션의 코드와 마찬가지로 다른 스레드가 종료되기를 기다리는 것을 의미합니다. 메인 스레드가 종료하기 전에 T1과 T2가 끝날 때까지 기다릴 수 있기를 원합니다. 끝나지 않으면 메인 스레드가 차단됩니다.
패키지 테스트; 공개 클래스 테스트 {공개 휘발성 정적 int i = 0; public static class addthread는 스레드 {@override public void run () {for (i = 0; i <100000000; i ++); }} public static void main (string [] args)은 중단 exception {addThread at = new addThread (); at.start (); at.join (); System.out.println (i); }} 위 코드의 AT.join이 제거되면 기본 스레드가 직접 실행되고 I의 값이 매우 작습니다. 가입이있는 경우 인쇄 된 값은 10000000이어야합니다.
그렇다면 Join은 어떻게 구현됩니까?
가입의 본질
while (isalive ())
{
대기 (0);
}
join () 메소드는 시간을 통과 할 수 있습니다. 즉, 제한된 기간을 기다리고이 시간 이후 자동으로 깨어납니다.
다음과 같은 질문이 있습니다 : 누가 스레드를 알릴까요? 스레드 클래스에서 알림을 호출 할 곳이 없습니까?
Javadoc에서는 관련 설명이 발견되었습니다. 스레드가 완성되고 종료되면 NotifyAll 메소드가 호출되어 현재 스레드 인스턴스에서 대기하는 모든 스레드를 깨우십시오. 이 작업은 JVM 자체에 의해 수행됩니다.
따라서 Javadoc은 또한 스레드 인스턴스에서 대기 및 알림/Notifyall을 사용하지 않는 제안을 제공했습니다. JVM은 스스로 호출되므로 통화의 예상 결과와 다를 수 있습니다.
3. 데몬 스레드
쓰레기 수집 스레드 및 JIT 스레드와 같은 백그라운드에서 일부 체계적인 서비스를 조용히 완료하는 것은 데몬 스레드로 이해할 수 있습니다.
모든 비 데몬 프로세스가 Java 응용 프로그램 내에서 끝나면 Java Virtual Machine은 자연스럽게 종료됩니다.
파이썬으로 구현하는 방법에 대한 기사를 작성하여 여기에서 확인했습니다.
자바에서 데몬이되는 것은 비교적 간단합니다.
스레드 t = new Daemont ();
T. 세트 데몬 (true);
t.start ();
이것은 데몬 스레드를 엽니 다.
패키지 테스트; 공개 클래스 테스트 {public static class daemonthread는 스레드 {@override public void run () {for (int i = 0; i <100000000; i ++) {system.out.println ( "hi"); }}} public static void main (string [] args)은 InterruptedException {daemonthread dt = new Daemonthread (); dt.start (); }} 스레드 DT가 데몬 스레드가 아닌 경우, 실행 후 콘솔 출력을 볼 수 있습니다.
시작하기 전에 가입 할 때
dt.setdaemon (true);
콘솔은 직접 종료되어 출력이 없습니다.
4. 스레드 우선 순위
스레드 클래스에는 스레드 우선 순위를 정의하는 3 가지 변수가 있습니다.
공개 최종 정적 int min_priority = 1; 공개 최종 정적 int norm_priority = 5; 공개 최종 정적 int max_priority = 10; 패키지 테스트; 공개 클래스 테스트 {public static class high 스레드 {static int count = 0; @override public void run () {while (true) {synchronized (test.class) {count ++; if (count> 100000000) {System.out.println ( "High"); 부서지다; }}}}} public static class low는 스레드 {static int count = 0; @override public void run () {while (true) {synchronized (test.class) {count ++; if (count> 100000000) {system.out.println ( "low"); 부서지다; }}}}} public static void main (String [] args)은 InterruptedException {High High = New High (); 낮은 낮은 = 새로운 낮은 (); High.setPriority (thread.max_priority); low.setpriority (thread.min_priority); low.start (); High.start (); }} 우선 순위가 높은 스레드와 우선 순위가 낮은 스레드가 동시에 잠금을 위해 경쟁하고 어느 것이 먼저 완료되는지 확인하십시오.
물론 우선 순위가 높은 것이 먼저 완료 될 필요는 없습니다. 여러 번 실행 한 후 우선 순위가 높을 확률이 비교적 높지만 우선 순위가 낮은 상태에서 먼저 완료 할 수 있습니다.
5. 기본 스레드 동기화 작업
동기화 및 Object.wait () obejct.notify ()
이 섹션에 대한 자세한 내용은 이전에 쓴 블로그를 참조하십시오.
주목해야 할 것은
동기화 된 잠금 방법에는 세 가지가 있습니다.
잠긴 객체를 지정하십시오 : 주어진 객체를 잠그고 동기화 코드를 입력하기 전에 주어진 객체의 잠금을 얻습니다.
인스턴스 방법에 직접 작용 : 현재 인스턴스를 잠그는 것과 같습니다. 동기화 코드를 입력하기 전에 현재 인스턴스의 잠금을 얻어야합니다.
정적 방법에 직접 작용 : 현재 클래스를 잠그는 것과 같습니다. 동기 코드를 입력하기 전에 현재 클래스의 잠금을 얻어야합니다.
인스턴스 메소드에서 작동하는 경우 새로운 두 가지 다른 인스턴스가 아닙니다.
추가 잠금이 클래스 클래스이기 때문에 클래스가 동일하다면 정적 메소드에서 작동하며 두 가지 인스턴스가 새로운 경우가 될 수 있습니다.
대기 및 알림 사용 방법 :
모든 잠금을 사용하고 대기 대기 및 알림을 사용하십시오
이 기사에서는 자세히 설명하지 않을 것입니다.