목차 (?) [-]
하나는 Javalangthread 클래스 2를 확장합니다. javalangrunnable 인터페이스 스레드와 실행 가능한 4 개의 스레드 상태 전환 사이의 3 가지 차이점 5 스레드 스케줄 스케줄링 6 가지 공통 기능 사용 방법을 설명하는 방법 7 개의 공통 스레드 명사 설명 8 스레드 데이터 전송 9 스레드 데이터 전송을 설명합니다.
이 기사는 주로 Java, 스레드 동기화, 스레드 데이터 전송, 스레드 상태 및 해당 스레드 기능 사용 및 개요의 멀티 스레딩 사용 방법에 대해 설명합니다.
먼저 프로세스와 스레드의 차이점에 대해 이야기 해 봅시다.
프로세스 : 각 프로세스에는 독립적 인 코드 및 데이터 공간 (프로세스 컨텍스트)이 있으며 프로세스 간 전환은 큰 오버 헤드가됩니다. 프로세스에는 1-N 스레드가 포함되어 있습니다.
글타래 (쓰레드) : 동일한 유형의 스레드 공유 코드 및 데이터 공간. 각 스레드에는 독립적 인 러닝 스택 및 프로그램 카운터 (PC)가 있으며 스레드 전환 오버 헤드는 작습니다.
프로세스와 마찬가지로 실은 생성, 준비, 달리기, 차단 및 종료의 5 단계로 나뉩니다.
다중 프로세스는 운영 체제가 동시에 여러 작업 (프로그램)을 실행할 수 있음을 의미합니다.
멀티 스레딩은 동일한 프로그램에서 여러 순차 스트림이 실행되는 것을 나타냅니다.
Java에는 멀티 스레딩을 구현하는 두 가지 방법이 있습니다. 하나는 스레드 클래스를 계속하고 다른 하나는 실행 가능한 인터페이스를 구현하는 것입니다.
1. Java.lang.thread 클래스를 확장하십시오
package com.multithread.learning;/***@functon multithreading learning*@저자 Lin Bingwen*@time 2015.3.9*/class Thread1은 스레드 {private String name; public Thread1 (문자열 이름) {this.name = 이름; } public void run () {for (int i = 0; i <5; i ++) {system.out.println (name + "run :" + i); try {sleep ((int) math.random () * 10); } catch (InterruptedException e) {e.printstacktrace (); }}}} public class main {public static void main (String [] args) {Thread1 mth1 = new Thread1 ( "a"); Thread1 mth2 = new Thread1 ( "b"); mth1.start (); mth2.start (); }} 산출:
달리기 : 0
B 런 : 0
달리기 : 1
달리기 : 2
달리기 : 3
달리기 : 4
B 런 : 1
B 런 : 2
B 런 : 3
B 런 : 4
다시 실행하십시오 :
달리기 : 0
B 런 : 0
B 런 : 1
B 런 : 2
B 런 : 3
B 런 : 4
달리기 : 1
달리기 : 2
달리기 : 3
달리기 : 4
설명 :
프로그램이 메인을 시작하고 실행하면 Java 가상 머신이 프로세스를 시작하고 메인 스레드 메인은 main ()을 호출 할 때 생성됩니다. Mitisay의 두 객체의 시작 방법으로 다른 두 스레드도 시작하여 전체 응용 프로그램이 여러 스레드에서 실행됩니다.
참고 : start () 메소드는 다중 스레드 코드를 즉시 실행하지 말고 대신 스레드를 실행 가능한 상태가되도록합니다. 실행될 때 운영 체제에 의해 결정됩니다.
프로그램 실행 결과에서 멀티 스레드 프로그램이 순서대로 실행된다는 것을 알 수 있습니다. 따라서 주문 외부로 실행 된 코드만이 다중 스레드로 설계되어야합니다.
Thread.sleep () 메소드 호출의 목적은 현재 스레드가 프로세스만으로 얻은 CPU 리소스를 점유하지 않도록하여 다른 스레드가 실행되기에 일정 시간을 남기는 것입니다.
실제로, 모든 다중 스레드 코드의 실행 순서는 불확실하며 각 실행 결과는 무작위입니다.
그러나 시작 방법을 반복적으로 호출하면 java.lang.ilegalthreadstateException이 발생합니다.
Thread1 mth1 = new Thread1 ( "a"); Thread1 mth2 = mth1; mth1.start (); mth2.start ();
산출:
스레드의 예외 "main"java.lang.ilegalthreadstateException의 예외입니다
at java.lang.thread.start (알 수없는 출처)
at com.multithread.learning.main.main (main.java:31)
달리기 : 0
달리기 : 1
달리기 : 2
달리기 : 3
달리기 : 4
2. java.lang.runnable 인터페이스를 구현하십시오
/***@functon 멀티 스레딩 학습*@저자 Lin Bingwen*@time 2015.3.9*/package com.multithread.runnable; class stread2 emplements runnable {private String name; public Thread2 (문자열 이름) {this.name = 이름; } @override public void run () {for (int i = 0; i <5; i ++) {system.out.println (name + "run :" + i); try {thread.sleep ((int) math.random () * 10); } catch (InterruptedException e) {e.printstacktrace (); }}}} public class main {public static void main (string [] args) {new Thread (new Thread2 ( "C"). start (); 새 스레드 (new Thread2 ( "d")). start (); }} 산출:
C 실행 : 0
D 런 : 0
D 런 : 1
C 실행 : 1
D 달리기 : 2
C 실행 : 2
D 달리기 : 3
C 실행 : 3
D 런 : 4
C 실행 : 4
설명 :
Thread2 클래스는 런닝 가능한 인터페이스를 구현하여 클래스가 다중 스레드 클래스의 특성을 갖습니다. run () 메소드는 다중 스레드 프로그램을위한 컨벤션입니다. 모든 다중 스레드 코드는 실행 메소드에 있습니다. 스레드 클래스는 실제로 런닝 가능한 인터페이스를 구현하는 클래스입니다.
멀티 스레딩을 시작할 때 먼저 스레드 클래스 생성자 스레드 (runnable target)를 통해 객체를 구성한 다음 스레드 개체의 start () 메소드를 호출하여 멀티 스레드 코드를 실행해야합니다.
실제로 모든 멀티 스레드 코드는 실행 스레드의 start () 메소드를 실행하여 실행됩니다. 따라서, 스레드 클래스를 확장하거나 런닝 가능한 인터페이스를 구현하여 멀티 스레딩을 구현하거나 스레드 객체 API를 통해 스레드를 제어하는 것이 스레드 클래스 API에 익숙해지는 것이 멀티 스레드 프로그래밍의 기초입니다.
3. 스레드와 실행 가능의 차이
클래스가 스레드를 상속하면 리소스 공유에 적합하지 않습니다. 그러나 실행 가능한 인터페이스가 구현되면 리소스 공유를 쉽게 구현할 수 있습니다.
패키지 com.multithread.Learning;/***@functon 멀티 스레드 학습, 상속 스레드, 리소스를 공유 할 수 없습니다*@awork 린 bingwen*@time 2015.3.9*/클래스 스레드 1 스레드 {private int count = 5; 개인 문자열 이름; public Thread1 (문자열 이름) {this.name = 이름; } public void run () {for (int i = 0; i <5; i ++) {system.out.println (name + "run count =" + count-); try {sleep ((int) math.random () * 10); } catch (InterruptedException e) {e.printstacktrace (); }}}} public class main {public static void main (String [] args) {Thread1 mth1 = new Thread1 ( "a"); Thread1 mth2 = new Thread1 ( "b"); mth1.start (); mth2.start (); }} 산출:
b 런 카운트 = 5
런 카운트 = 5
b 런 카운트 = 4
B 런 카운트 = 3
B 런 카운트 = 2
b 런 카운트 = 1
실행 카운트 = 4
실행 카운트 = 3
실행 카운트 = 2
실행 카운트 = 1
위에서, 우리는 카운트가 다른 스레드마다 다르다는 것을 알 수 있으며, 이는 티켓 판매 시스템에 큰 문제가 될 것입니다. 물론 여기에서 동기화를 사용할 수 있습니다. 여기에서 실행하기 위해 사용합시다
/***@functon 멀티 스레딩 학습을 상속받을 수 있습니다. 자원은 공유 할 수 있습니다*@저자 Lin Bingwen*@Time 2015.3.9*/패키지 com.multithread.runnable; class strand2 emplements runnable {private int count = 15; @override public void run () {for (int i = 0; i <5; i ++) {system.out.println (thread.currentthread (). getName () + "run count =" + count-); try {thread.sleep ((int) math.random () * 10); } catch (InterruptedException e) {e.printstacktrace (); }}}}} public class main {public static void main (string [] args) {Thread2 my = new Thread2 (); 새 스레드 (내, "c"). start (); // 같은 mt이지만 스레드에서는 불가능합니다. 객체 mt를 인스턴스화하면 예외가 새 스레드 (내, "d"). start (); 새 스레드 (내, "e"). start (); }} 산출:
C 실행 카운트 = 15
D 런 카운트 = 14
e run count = 13
D 런 카운트 = 12
D 런 카운트 = 10
D 런 카운트 = 9
D 런 카운트 = 8
C 실행 카운트 = 11
e run count = 12
C 실행 카운트 = 7
e run count = 6
C 실행 카운트 = 5
e run count = 4
C 실행 카운트 = 3
e run count = 2
여기서 우리는 각 스레드가 동일한 인스턴스화 객체를 사용한다는 점에 유의해야합니다. 동일하지 않은 경우 효과는 위와 동일합니다!
요약 :
스레드 클래스 상속을 통해 실행 가능한 인터페이스를 구현하는 장점 :
1) : 동일한 프로그램 코드가있는 여러 스레드에 적합한 리소스를 처리합니다.
2) : Java의 단일 상속 제한을 피할 수 있습니다.
3) : 프로그램의 견고성을 높이고, 코드는 여러 스레드로 공유 할 수 있으며 코드와 데이터는 독립적입니다.
메인 메소드는 실제로 스레드입니다. Java에서는 스레드가 동시에 시작됩니다. 언제, 어느시기에, 어느 쪽이 실행되는지에 관해서는, 누가 CPU 자원을 먼저 얻는 지에 따라 다릅니다.
Java에서는 프로그램이 실행될 때마다 최소 2 개의 스레드가 시작됩니다. 하나는 메인 스레드이고 다른 하나는 쓰레기 수집 스레드입니다. Java 명령을 사용하여 클래스를 실행할 때마다 JVM이 실제로 시작되고 각 JVM 인턴쉽은 운영 체제에서 프로세스를 시작합니다.
4. 스레드 상태 전환
1. New State (New) : 새 스레드 객체가 생성됩니다.
2. Ready State (runnable) : 스레드 객체가 생성 된 후 다른 스레드는 객체의 start () 메소드를 호출합니다. 이 상태의 스레드는 런닝 가능한 스레드 풀에 있으며 CPU 사용 권한을 얻기 위해 기다릴 수있게됩니다.
3. 실행 상태 : 준비 상태의 스레드는 CPU를 획득하고 프로그램 코드를 실행합니다.
4. 차단 된 상태 : 차단 된 상태는 스레드가 어떤 이유로 CPU 사용 권한을 포기하고 일시적으로 실행을 중지한다는 것을 의미합니다. 스레드가 준비 상태에 들어가기 전까지는 달리기 상태로 갈 기회가 없습니다. 막힘에는 세 가지 유형이 있습니다.
(1) 대기 대기 차단 : 실행중인 스레드가 Wait () 메소드를 실행하고 JVM은 스레드를 대기 풀에 넣습니다.
(2) 동기 차단 : 러닝 스레드가 객체의 동기화 잠금을 획득 할 때, 동기화 잠금이 다른 스레드에 의해 점유되면 JVM은 스레드를 잠금 풀에 넣습니다.
(iii), 기타 차단 : 러닝 스레드가 수면 () 또는 join () 메소드를 실행하거나 I/O 요청을 발행하면 JVM은 스레드를 차단 상태로 설정합니다. Sleep () State가 시간이 초과되면 join ()는 스레드가 끝나거나 시간이 끝나기를 기다렸거나 I/O 처리가 완료되면 스레드가 준비 상태에 다시 들어갔습니다.
5. Dead State : 스레드가 예외로 인해 Run () 메소드를 실행하거나 종료했으며 스레드는 수명주기를 종료했습니다.
5. 스레드 스케줄링
스레드 스케줄링
1. 스레드 우선 순위 조정 : Java 스레드는 우선 순위가 있으며 우선 순위가 높은 스레드는 더 많은 기회를 얻을 수 있습니다.
Java 스레드의 우선 순위는 정수로 표시되며 값 범위는 1 ~ 10입니다. 스레드 클래스에는 다음 세 가지 정적 상수가 있습니다.
정적 int max_priority
스레드가 가질 수있는 최우선 과제는 10입니다.
정적 int min_priority
스레드가 가질 수있는 가장 낮은 우선 순위는 1입니다.
정적 int norm_priority
스레드에 할당 된 기본 우선 순위는 5입니다.
스레드 클래스의 setPriority () 및 getPriority () 메소드는 스레드의 우선 순위를 각각 설정하고 얻는 데 사용됩니다.
각 스레드의 기본 우선 순위가 있습니다. 기본 스레드의 기본 우선 순위는 Thread.norm_priority입니다.
스레드의 우선 순위는 상속됩니다. 예를 들어 스레드 B가 스레드 A에서 생성되면 B는 A와 동일한 우선 순위를 갖습니다.
JVM은 10 개의 스레드 우선 순위를 제공하지만 공통 운영 체제와 잘 매핑되지는 않습니다. 프로그램이 각 운영 체제에 포팅 되려면 다음 세 가지 정적 상수의 스레드 클래스를 우선 순위로 사용해야합니다. 이는 동일한 우선 순위가 동일한 스케줄링 방법을 채택 할 수 있습니다.
2. 스레드 수면 : Thread.Sleep (Long Millis) 방법 스레드를 차단 상태로 이동하도록합니다. Millis 매개 변수는 수면 시간을 밀리 초로 설정합니다. 수면이 끝나면 실행 가능해집니다. Sleep () 플랫폼은 휴대 성이 좋습니다.
3. 스레드 대기 : 객체 클래스의 대기 () 메소드는 다른 스레드가 객체의 notify () 메소드 또는 notifyall () 웨이크업 메소드를 호출 할 때까지 현재 스레드가 대기하게됩니다. 이 두 개의 모닝 방법은 또한 객체 클래스의 방법이며, 그들의 동작은 대기 (0) 호출과 동일합니다.
4. 스레드 양보 : thread.yield () 메소드는 현재 실행중인 스레드 객체를 일시 중지하고 실행 기회를 동일하거나 우선 순위로 스레드에 제공합니다.
5. 스레드 join : join () 메소드, 다른 스레드가 종료되기를 기다립니다. 현재 스레드에서 다른 스레드의 join () 메소드를 호출하면 현재 스레드는 다른 프로세스가 실행될 때까지 차단 상태로 이동하고 현재 스레드는 차단에서 준비 상태로 이동합니다.
6. 스레드 웨이크 업 : 객체 클래스의 Notify () 메소드는이 객체 모니터에서 대기하는 단일 스레드를 깨우냅니다. 모든 스레드 가이 객체에서 대기하는 경우 스레드 중 하나가 선택됩니다. 선택은 임의적이며 구현을 결정할 때 발생합니다. 스레드는 대기 방법 중 하나를 호출하여 객체의 모니터에서 대기합니다. 현재 스레드 가이 객체의 잠금 장치를 버릴 때까지 깨어 난 스레드를 실행할 수 없습니다. Waken Thread는 기존 방식으로 객체에 적극적으로 동기화되는 다른 모든 스레드와 경쟁합니다. 예를 들어, Waken Thread는이 객체를 잠그는 다음 스레드라는 신뢰할 수있는 권한이나 단점이 없습니다. 비슷한 방법에는이 객체 모니터에서 대기하는 모든 스레드를 깨우는 NotifyAll ()도 있습니다.
참고 : 스레드의 SUSTEND () 및 RESUME ()가 JDK1.5에서 폐지되었으며 다시 도입되지 않습니다. 교착 상태가 있기 때문에.
6. 일반적인 기능에 대한 설명
① 수면 (Long Millis) : 현재 실행중인 스레드가 지정된 밀리 초 내에서 잠을 자도록하십시오 (SUSTEND EXECUTION)
② join () : t 스레드가 종료되기를 기다리는 것을 말합니다.
사용하는 방법.
Join은 스레드 클래스의 메소드입니다. 스레드를 시작한 직후에 호출됩니다. 즉, join ()의 함수는 다음과 같습니다. "스레드가 끝날 때까지 기다리십시오". 여기서 이해해야 할 것은 스레드가 자식 스레드가 종료되기를 기다리는 기본 스레드를 지칭한다는 것입니다. 즉, 자식 스레드 후 코드는 join () 메소드를 호출하며 자식 스레드가 완료 될 때까지만 실행할 수 있습니다.
스레드 t = new athread (); t.start (); t.join ();
join () 메소드를 사용하는 이유는 무엇입니까?
대부분의 경우, 메인 스레드는 자식 스레드를 생성하고 시작합니다. 어린이 스레드에서 많은 시간이 소요되는 작업이 필요한 경우, 주 스레드는 종종 자식 스레드 전에 끝납니다. 그러나 기본 스레드가 다른 트랜잭션을 처리 한 후 하위 스레드의 처리 결과를 사용해야하는 경우, 즉 메인 스레드는 하위 스레드가 종료되기 전에 실행을 완료 할 때까지 기다려야합니다. 현재 join () 메소드를 사용해야합니다.
가입 없음. /** *@functon multithreading Learning, join *@joinge lin bingwen *@time 2015.3.9 */package com.multithread.join; class Thread1은 스레드 {private String name; public Thread1 (문자열 이름) {super (name); this.name = 이름; } public void run () {system.out.println (thread.currentThread (). getName () + "스레드 시작!"); for (int i = 0; i <5; i ++) {system.out.println ( "subthread"+name+"run :"+i); try {sleep ((int) math.random () * 10); } catch (InterruptedException e) {e.printstacktrace (); }} system.out.println (thread.currentThread (). getName () + "스레드 런 엔드!"); }} public class main {public static void main (string [] args) {System.out.println (Thread.CurrentThread (). getName ()+"기본 스레드 실행 시작!"); Thread1 mth1 = new Thread1 ( "a"); Thread1 mth2 = new Thread1 ( "b"); mth1.start (); mth2.start (); System.out.println (thread.currentthread (). getName ()+ "메인 스레드 실행 끝!"); }} 출력 결과 :
메인 메인 스레드가 실행되기 시작합니다!
메인 메인 스레드가 실행됩니다!
b 스레드 실행이 시작됩니다!
아동 스레드 B 실행 : 0
스레드 런이 시작됩니다!
자식 실 A 달리기 : 0
아동 스레드 B 실행 : 1
어린이 실 A 달리기 : 1
어린이 실 A 달리기 : 2
어린이 실 A 달리기 : 3
어린이 실 A 달리기 : 4
스레드 실행 끝!
아동 스레드 B 실행 : 2
아동 스레드 B 실행 : 3
아동 스레드 B 실행 : 4
b 스레드가 넘어집니다!
메인 스레드가 자식 스레드보다 일찍 끝났다는 것을 알았습니다.
가입하다
public class main {public static void main (string [] args) {System.out.println (thread.currentThread (). getName ()+"기본 스레드 실행 시작!"); Thread1 mth1 = new Thread1 ( "a"); Thread1 mth2 = new Thread1 ( "b"); mth1.start (); mth2.start (); try {mth1.join (); } catch (InterruptedException e) {e.printstacktrace (); } try {mth2.join (); } catch (InterruptedException e) {e.printstacktrace (); } system.out.println (Thread.currentThread (). getName ()+ "메인 스레드 실행이 끝!"); }} 실행 결과 :
메인 메인 스레드가 실행되기 시작합니다!
스레드 런이 시작됩니다!
자식 실 A 달리기 : 0
b 스레드 실행이 시작됩니다!
아동 스레드 B 실행 : 0
어린이 실 A 달리기 : 1
아동 스레드 B 실행 : 1
어린이 실 A 달리기 : 2
아동 스레드 B 실행 : 2
어린이 실 A 달리기 : 3
아동 스레드 B 실행 : 3
어린이 실 A 달리기 : 4
아동 스레드 B 실행 : 4
스레드 실행 끝!
메인 스레드는 자식 스레드가 끝나기 전에 완료 될 때까지 확실히 기다립니다.
③yield () : 현재 실행중인 스레드 개체를 일시 중지하고 다른 스레드를 실행합니다.
thread.yield () 메소드의 함수는 다음과 같습니다. 현재 실행중인 스레드 개체를 일시 중지하고 다른 스레드를 실행합니다.
수율 ()가해야 할 일은 현재 실행 스레드를 런닝 가능한 상태로 되 돌려서 우선 순위가 동일한 다른 스레드가 실행 기회를 얻을 수 있도록하는 것입니다. 따라서, Oquile ()을 사용하는 목적은 동일한 우선 순위의 스레드가 적절하게 수행 할 수 있도록하는 것입니다. 그러나 실제로는 양보 스레드가 스레드 스케줄러에 의해 다시 선택 될 수 있기 때문에 수율 ()은 양보의 목적을 달성 할 수 없습니다.
결론 : 수율 ()은 스레드가 대기/수면/차단 상태로 이동하지 않습니다. 대부분의 경우, 수율 ()은 스레드가 실행 상태에서 실행되는 상태로 이동하지만 작동하지 않을 수 있습니다. 위의 그림을 볼 수 있습니다.
/** *@functon multithreading 학습 수익률 *@저자 Lin Bingwen *@time 2015.3.9 */package com.multithread.yield; class Threadyield extends 스레드 {public stroodyield (string name) {super (name); } @override public void run () {for (int i = 1; i <= 50; i ++) {system.out.println ( "" + this.getName () + "-----" + i); // 내가 30 일 때, 스레드는 CPU 시간을 포기하고 다른 스레드 또는 그 자체의 스레드가 실행되도록합니다 (즉, (i == 30) {this.yield (); }}}} public class main {public static void main (String [] args) {Threadyield yt1 = new Threadyield ( "Zhang San"); Threadyield yt2 = New Threadyield ( "li si"); yt1.start (); yt2.start (); }} 실행 결과 :
첫 번째 사례 : Li Si (스레드)는 CPU 시간이 30으로 실행될 때 시간을 초과합니다.이 시점에서 Zhang San (스레드)은 CPU 시간을 잡고 실행합니다.
두 번째 상황 : Li Si (스레드)가 30으로 실행되면 CPU 시간이 버려집니다. 현재 Li Si (스레드)는 CPU 시간을 잡고 실행합니다.
수면 ()과 수율 ()의 차이
sleep ()와 partion ())의 차이점 : sleep ()는 현재 스레드가 정체 상태로 들어가게되므로 Sleep ()를 실행하는 스레드는 지정된 시간 내에 확실히 실행되지 않습니다. 수율 ()은 현재 스레드가 실행 가능한 상태로 돌아 오게되므로 실행 가능한 상태에 입력 한 직후에 실행 된 스레드를 실행하는 스레드가 실행될 수 있습니다.
수면 방법은 현재 실행중인 스레드가 일정 기간 동안 잠을 자게하고 업적 할 수없는 상태로 들어갑니다. 이 기간의 길이는 프로그램에 의해 설정됩니다. 수율 방법을 사용하면 현재 스레드가 CPU 소유권을 포기할 수 있지만 전송 시간은 불가능합니다. 실제로, 수율 () 메소드는 다음 작업에 해당합니다. 먼저, 현재 동일한 런닝 가능한 상태에있는 우선 순위가 동일한 스레드가 있는지 확인하십시오. 그렇다면 CPU 소유권을이 스레드에 넘겨주십시오. 그렇지 않으면 원래 스레드를 계속 실행하십시오. 따라서 수율 () 메소드는 "양보"라고하며, 이는 동일한 우선 순위를 가진 다른 스레드에 대한 기회를 제공합니다.
또한, 수면 방법을 사용하면 우선 순위가 낮은 스레드가 실행 기회를 얻을 수 있지만, 수율 () 메소드가 실행되면 현재 스레드는 여전히 실행 가능한 상태에 있으므로 나중에 CPU 소유권을 얻기 위해 더 낮은 우선 순위 스레드를 포기할 수 없습니다. 실행 시스템에서 우선 순위가 높은 스레드가 수면 메소드를 호출하지 않고 I/O에 의해 차단되지 않으면, 우선 순위가 낮은 스레드는 모든 높은 우선 순위 스레드가 실행될 때까지 기다릴 수 있습니다.
④setPriority () : 스레드의 우선 순위를 변경합니다.
Min_Priority = 1
NORM_PRIORITY = 5
max_priority = 10
용법:
Thread4 t1 = new Thread4 ( "T1");
Thread4 t2 = new Thread4 ( "T2");
t1.setpriority (thread.max_priority);
t2.setpriority (thread.min_priority);
⑤ interrupt () : 스레드를 방해합니다. 이 결말 방법은 다소 거칠다. T 스레드가 자원을 열고 닫을 시간이 없으면, 즉 실행 방법이 실행되기 전에 스레드를 종료해야하므로 리소스가 닫히지 못하게됩니다.
프로세스를 끝내는 가장 좋은 방법은 Sleep () 함수의 예제 프로그램을 사용하는 것입니다. 부울 변수는 스레드 클래스에서 run () 메소드가 종료 될 때 제어하는 데 사용됩니다. run () 메소드가 끝나면 스레드가 끝납니다.
wait ()
obj.wait () 및 obj.notify ()는 동기화 된 (OBJ)와 함께 사용해야합니다. 동기화 된 관점에서 obj.wait ()이고 obj.notify는 동기화 된 (OBJ) {...} 문장에 있어야합니다. 기능적 관점에서 대기는 스레드가 객체 잠금을 획득 한 후 객체 잠금을 적극적으로 공개하고 스레드가 잠을자는 것을 의미합니다. 객체 잠금은 얻을 수 없으며 다른 스레드가 객체 Notify ()을 호출하여 스레드를 깨우기 시작할 때까지 실행이 계속됩니다. 해당 Notify ()는 객체 잠금의 모닝 작업입니다. 그러나 Notify () 호출 후에는 객체 잠금이 즉시 릴리스되지 않지만 해당 Synchronized () {} 문장의 실행이 완료되고 잠금이 자동으로 릴리스되며 JVM은 대기 () 개체 잠금 스레드에서 스레드를 무작위로 선택하고 객체 잠금에 할당하고 실행을 계속한다는 것입니다. 이는 스레드 간의 동기화 및 웨이크 업 작업을 제공합니다. Thread.Sleep () 및 Object.Wait () 모두 현재 스레드를 일시 중지하고 CPU 컨트롤을 해제 할 수 있습니다. 주요 차이점은 Object.wait ()가 CPU를 출시하는 동안 객체 잠금의 제어를 방출한다는 것입니다.
개념적으로 이해하는 것만으로는 충분하지 않으며, 더 잘 이해하기 위해 실제 예에서 테스트해야합니다. Object.wait () 및 Object.notify ()의 적용의 가장 전형적인 예는 3 개의 스레드로 ABC를 인쇄하는 데 문제가 있어야합니다. 이것은 비교적 고전적인 인터뷰 질문이며 질문은 다음과 같습니다.
3 개의 스레드를 설정하고, A를 10 번, 스레드 B 인쇄 B를 10 번, 스레드 C 인쇄 C 10 번, 스레드 C는 동시에 실행해야하며 ABC는 10 번 교대로 인쇄됩니다. 이 문제는 Object 's Wait () 및 notify ()를 사용하여 쉽게 해결할 수 있습니다. 코드는 다음과 같습니다.
/** * 사용 대기 사용 * @author dreamsea * @time 2015.3.9 */package com.multithread.wait; public class mythreadprinter2 implements runnable {private String name; 개인 대상 이전; 개인 대상 자아; private mythreadprinter2 (문자열 이름, 객체 이전, 객체 자체) {this.name = name; this.prev = 이전; this.self = 자기; } @override public void run () {int count = 10; while (count> 0) {synchronized (prev) {synchronized (self) {system.out.print (name); 세다--; self.notify (); } try {prev.wait (); } catch (InterruptedException e) {e.printstacktrace (); }}}} public static void main (string [] args)은 예외 {object a = new Object (); Object B = New Object (); Object C = New Object (); MythreadPrinter2 PA = New MythreadPrinter2 ( "A", C, A); MythreadPrinter2 PB = New MythreadPrinter2 ( "B", A, B); MythreadPrinter2 PC = New MythreadPrinter2 ( "C", B, C); 새 스레드 (PA) .start (); Thread.sleep (100); // 새 스레드 (pb) .start ()를 실행해야합니다. Thread.sleep (100); }} 출력 결과 :
abcabcabcabcabcabcabcabcabcabcabc
먼저 전반적인 아이디어를 설명해 봅시다. 일반적인 관점에서 볼 때,이 문제는 세 스레드 사이의 동기식 웨이크 업 작업입니다. 주된 목적은 Threada-> ThreadB-> ThreadC-> Threada 루프에서 3 개의 스레드를 실행하는 것입니다. 스레드 실행 순서를 제어하려면 웨이크 업 및 대기 순서를 결정해야하므로 각 스레드는 실행을 계속하기 전에 동시에 두 개의 객체 잠금을 고정해야합니다. 객체 잠금은 이전 스레드에서 보유한 객체 잠금 장치입니다. 다른 하나는 물체의 잠금입니다. 주요 아이디어는 실행 순서를 제어하기 위해서는 먼저 이전 잠금 장치를 고정해야한다는 것입니다. 즉, 이전 스레드는 자체 객체 잠금을 해제 한 다음 자체 객체 잠금을 적용해야합니다. 둘 다일 때 인쇄하십시오. 그런 다음 먼저 self.notify ()를 호출하여 자체 객체 잠금을 해제하고 다음 대기 스레드를 깨우고 다음 스레드를 전화 한 다음 prev.wait ()을 호출하여 이전 객체 잠금을 풀고 현재 스레드를 끝내고 루프가 다시 깨어날 때까지 기다립니다. 위의 코드를 실행하면 3 개의 스레드가 총 10 번 루프에서 ABC를 인쇄한다는 것을 알 수 있습니다. 프로그램 실행의 주요 과정은 스레드 A가 가장 먼저 실행되고 C와 A의 객체 잠금을 고정 한 다음 A와 C의 잠금 장치를 방출 한 다음 B를 깨우고 B가 잠금 A를 기다린 다음 잠금 B에 적용한 다음 B에 적용한 다음 B, 잠금 C, 잠금 C, 잠금 C에 적용됩니다. 문제이지만 신중하게 생각하면 초기 조건 인 문제가 있음을 알게됩니다. 세 가지 스레드는 A, B 및 C의 순서로 시작됩니다. 이전의 생각에 따르면, A는 B, B는 C, C를 깨우고 A가 깨어납니다. 그러나이 가정은 JVM의 스레드 예약 및 실행 순서에 달려 있습니다.
대기와 수면의 차이
일반적인 요점 :
1. 그들은 모두 다중 스레드 환경에 있으며 프로그램 통화 및 반환시 지정된 밀리 초를 차단할 수 있습니다.
2. Wait ()와 sleep ()는 모두 인터럽트 () 메소드를 통해 스레드의 일시 정지 상태를 방해하여 스레드가 즉시 인터럽트 한 외환을 던질 수 있습니다.
스레드 A가 스레드 B를 즉시 종료하려는 경우 스레드 B에 해당하는 스레드 인스턴스에서 인터럽트 메소드를 호출 할 수 있습니다. 스레드 B가 대기 중/수면/조인 된 경우 스레드 B가 즉시 인터럽트 한 외환을 던지고 Catch () {}에 직접 반환하여 스레드를 안전하게 종료합니다.
인터럽트 () 메소드가 아니라 내부에서 스레드 자체에 의해 인터럽트 예고가 발생한다는 점에 유의해야합니다. 스레드에서 인터럽트 ()가 호출되면 스레드가 일반 코드를 실행하는 경우 스레드가 인터럽트 한 소집을 전혀 던지지 않습니다. 그러나 스레드가 Wait ()/sleep ()/join ()에 들어가면 인터럽트 꺼짐이 즉시 발생됩니다.
차이점 :
1. 스레드 클래스 방법 : sleep (), 수율 () 등
객체 방법 : 대기 () 및 notify () 등
2. 각 객체에는 동기식 액세스를 제어하기위한 잠금 장치가 있습니다. 동기화 된 키워드는 객체의 잠금과 상호 작용하여 스레드 동기화를 실현할 수 있습니다.
수면 방법은 잠금을 해제하지 않으며 대기 방법은 잠금을 방출하여 다른 스레드가 동기 제어 블록 또는 메소드를 사용할 수 있도록합니다.
3. 기다림, 알림 및 노트시 알트는 동기화 제어 방법 또는 동기화 제어 블록에서만 사용할 수 있지만 수면은 어디서나 사용할 수 있습니다.
4. 수면은 예외를 포착해야하며, 대기, 알림 및 알림은 예외를 포착 할 필요가 없으므로 sleep ()와 Wait () 메소드의 가장 큰 차이점은 다음과 같습니다.
수면 ()가 잠을 자면 물체 잠금 장치를 유지하고 여전히 잠금 장치를 가지고 있습니다.
대기 ()가 잠들 때 객체 잠금 장치가 해제됩니다.
그러나 Wait () 및 sleep ()는 인터럽트 () 메소드를 통해 스레드의 일시 정지 상태를 방해 할 수 있으므로 스레드가 즉시 InterruptedException을 던지도록합니다 (그러나이 메소드를 사용하는 것이 좋습니다).
수면 () 방법
수면 ()은 현재 스레드가 정체 상태로 들어가게하여 컵 사용을 포기하고, 목적은 현재 스레드가 프로세스만으로 얻은 CPU 리소스를 점유하여 다른 스레드가 실행되기 위해 특정 시간을 남기지 않도록하는 것입니다.
수면 ()은 스레드 클래스의 정적 방법입니다. 따라서 객체의 기계 잠금을 변경할 수 없으므로 Sleep () 메소드를 동기화 된 블록에서 호출 할 때 스레드가 휴면 상태이지만 물체의 기계 잠금이 해제되지 않으며 다른 스레드는 객체에 액세스 할 수 없습니다 (잠들었을 때에도 개체 잠금을 고정하고 있음).
수면 () 수면 () 수면 시간이 만료 된 후에는 다른 스레드가 실행 중일 수 있고 스레드가 우선 순위가 높지 않으면 실행을 포기할 예정이 없기 때문에 스레드가 즉시 실행되는 것은 아닙니다.
대기 () 메소드
대기 () 메소드는 객체 클래스의 메소드입니다. 스레드가 Wait () 메소드를 실행하면 객체와 관련된 대기 풀로 들어가고 동시에 객체의 기계 잠금을 잃고 (릴리스) (일시적으로 기계 잠금을 잃어 버리고 대기 시간)도 객체 잠금을 반환해야 함); 다른 스레드가 액세스 할 수 있습니다.
대기 ()는 Notify 또는 NotifyAll 또는 지정된 수면 시간을 사용하여 현재 대기 풀에서 스레드를 깨우십시오.
Wiat ()는 동기화 된 블록에 배치해야합니다. 그렇지 않으면 프로그램 런타임이있을 때 "java.lang.ilegalmonitorstateexception"예외가 발생합니다.
7. 일반 스레드 용어 설명
메인 스레드 : JVM Call Program Main ()에 의해 생성 된 스레드.
현재 실 : 이것은 혼란스러운 개념입니다. 일반적으로 Thread.CurrentThread ()를 통해 얻은 프로세스를 나타냅니다.
배경 스레드 : 데몬 스레드라고도하는 다른 스레드에 서비스를 제공하는 스레드를 나타냅니다. JVM 쓰레기 수집 스레드는 배경 스레드입니다. 사용자 스레드와 데몬 스레드의 차이점은 기본 스레드의 끝에 따라 메인 스레드가 전경 스레드를 종료 할 때까지 기다릴지 여부입니다. 배경 스레드의 서비스를 수용하는 스레드를 나타냅니다. 실제로, 전경 배경 스레드는 꼭두각시와 비하인드 스토리 조작기의 관계와 마찬가지로 서로 연결됩니다. 꼭두각시는 전경 스레드이며, 비하인드 스토리 조작기는 배경 스레드입니다. 전경 스레드에 의해 생성 된 스레드도 기본적으로 전경 스레드입니다. isdaemon () 및 setDaemon () 메소드를 사용하여 스레드가 배경 스레드인지 결정하고 설정할 수 있습니다.
스레드 클래스의 몇 가지 일반적인 방법 :
수면 () : 실이 n 밀리 초로 잘수록됩니다.
isalive () : 스레드가 살아남는 지 결정합니다.
가입 () : 스레드가 끝날 때까지 기다립니다.
ActiveCount () : 프로그램의 활성 스레드 수.
enumerate () : 프로그램의 스레드 열거.
currentthread () : 현재 스레드를 가져옵니다.
isdaemon () : 스레드가 데몬 스레드인지 여부.
setDaemon () : 스레드를 데몬 스레드로 설정합니다. (사용자 스레드와 데몬 스레드의 차이점은 기본 스레드의 끝에 따라 메인 스레드가 종료 될 때까지 기다릴지 여부입니다)
setName () : 스레드의 이름을 설정합니다.
대기 () : 스레드를 기다리십시오.
notify () : 스레드가 계속 실행되도록 알립니다.
setPriority () : 스레드의 우선 순위를 설정합니다.
8. 스레드 동기화
1. 동기화 된 키워드의 두 가지 범위가 있습니다.
1) 객체 인스턴스 내에 있습니다. 동기화 된 Amethod () {}는 여러 스레드 가이 객체의 동기화 된 메소드에 동시에 액세스하는 것을 방지 할 수 있습니다 (한 스레드가 동기화 된 메소드 중 하나에 액세스하는 한 객체에 여러 동기화 된 메소드가있는 경우 다른 스레드는 동시에 객체의 동기화 된 메소드에 액세스 할 수 없습니다). 현재 다른 객체 인스턴스의 동기화 된 메소드는 중단되지 않습니다. That is to say, other threads can still access the synchronized method in another object instance of the same class at the same time;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;
3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
Java's support and synchronization mechanism for multithreading is very popular. It seems that using the synchronized keyword can easily solve the problem of multithreaded shared data synchronization. 정확히 뭐야? It is also necessary to have an in-depth understanding of the role of synchronized keywords before you can make a conclusion.
总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
在进一步阐述之前,我们需要明确几点:
A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁而且同步方法很可能还会被其他线程的对象访问。
B.每个对象只有一个锁(lock)与之相关联。
C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
接着来讨论synchronized用到不同地方对代码产生的影响:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。
1. 把synchronized当作函数修饰符时,示例代码如下:
Public synchronized void methodAAA(){//….}这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。
上边的示例代码等同于如下代码:
public void methodAAA(){synchronized (this) // (1){ //…..}}(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(
2.同步块,示例代码如下:
public void method3(SomeObject so) { synchronized(so){ //…..}}这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable{ private byte[] lock = new byte[0]; // 特殊的instance变量Public void methodA(){ synchronized(lock) { //… }}//…..}注:零长度的byte数组对象创建起来将比任何对象都经济查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo{public synchronized static void methodAAA() // 同步的static 函数{//….}public void methodBBB(){ synchronized(Foo.class) // class literal(类名称字面常量)} }代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将Foo.class和P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
九、线程数据传递
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。
9.1、通过构造方法传递数据在创建线程时,必须要建立一个Thread类的或其子类的实例。因此,我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来,以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据:
package mythread; public class MyThread1 extends Thread { private String name; public MyThread1(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { Thread thread = new MyThread1("world"); thread.start(); } }由于这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。因此,要想避免这种情况,就得通过类方法或类变量来传递数据。
9.2、通过变量和方法传递数据
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。下面的代码是对MyThread1类的改版,使用了一个setName方法来设置name变量:
package mythread; public class MyThread2 implements Runnable { private String name; public void setName(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { MyThread2 myThread = new MyThread2(); myThread.setName("world"); Thread thread = new Thread(myThread); thread.start(); }} 9.3、通过回调函数传递数据
上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,如在下面代码的run方法中产生了3个随机数,然后通过Work类的process方法求这三个随机数的和,并通过Data类的value将结果返回。从这个例子可以看出,在返回value之前,必须要得到三个随机数。也就是说,这个value是无法事先就传入线程类的。
package mythread; class Data { public int value = 0; } class Work { public void process(Data data, Integer numbers) { for (int n : numbers) { data.value += n; } } } public class MyThread3 extends Thread { private Work work; public MyThread3(Work work) { this.work = work; } public void run() { java.util.Random random = new java.util.Random(); Data data = new Data(); int n1 = random.nextInt(1000); int n2 = random.nextInt(2000); int n3 = random.nextInt(3000); work.process(data, n1, n2, n3); // 使用回调函数System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+" + String.valueOf(n3) + "=" + data.value); } public static void main(String[] args) { Thread thread = new MyThread3(new Work()); thread.start(); } }以上就是对Java 多线程的详解,希望能帮助你学习这部分知识,谢谢大家对本站的支持!