스레드는 운영 체제 작업의 기본 단위로 프로세스에 캡슐화되어 있습니다. 스레드를 수동으로 생성하지 않더라도 프로세스에는 기본 스레드가 실행됩니다.
JVM의 경우 실행할 단일 스레드 프로그램을 작성할 때 JVM에는 최소한 두 개의 스레드가 실행되고 있는데, 하나는 우리가 만든 프로그램이고 다른 하나는 가비지 수집입니다.
스레드 기본 정보
Thread.currentThread() 메서드를 통해 현재 스레드에 대한 일부 정보를 얻고 이를 수정할 수 있습니다.
다음 코드를 살펴보겠습니다.
Thread.currentThread().setName("테스트");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
이름 = Thread.currentThread().getName();
우선 순위 = Thread.currentThread().getPriority();
groupName = Thread.currentThread().getThreadGroup().getName();
isDaemon = Thread.currentThread().isDaemon();
System.out.println("스레드 이름:" + 이름);
System.out.println("우선순위:" + 우선순위);
GroupName , 각 스레드는 기본적으로 스레드 그룹에 속합니다. 스레드 그룹은 스레드와 스레드 그룹이 트리 구조를 형성하도록 하위 스레드 그룹을 포함할 수도 있습니다.
Name , 각 스레드에는 이름이 지정됩니다. 명시적으로 지정하지 않으면 이름 규칙은 "Thread-xxx"입니다.
Priority 각 스레드는 고유한 우선순위를 가지며 JVM의 우선순위 처리 방식은 "선점형"입니다. JVM은 우선순위가 높은 스레드를 찾으면 즉시 우선순위가 동일한 여러 스레드에 대해 스레드를 실행하고 이를 폴링합니다. Java의 스레드 우선순위 범위는 1에서 10까지이며 기본값은 5입니다. Thread 클래스는 가장 높은 우선순위와 가장 낮은 우선순위를 나타내는 MIN_PRIORITY 및 MAX_PRIORITY라는 두 가지 상수를 정의합니다.
우선 순위가 다른 두 개의 스레드를 정의하는 다음 코드를 볼 수 있습니다.
스레드 thread2 = 새 스레드("높음")
{
공개 무효 실행()
{
for (int i = 0; i < 5; i++)
{
System.out.println("스레드 2가 실행 중입니다.");
}
}
};
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
thread1.start();
}
스레드를 만드는 방법
위 내용은 모두 기본 스레드의 일부 정보를 보여줍니다. 스레드를 만드는 방법은 무엇입니까? Java에서는 스레드를 생성하는 세 가지 방법이 있습니다.
Java의 스레드는 Thread 클래스를 상속하거나 Runnable 인터페이스를 구현합니다.
내부 클래스를 사용하여 스레드 생성
내부 클래스를 사용하여 스레드를 생성할 수 있습니다. 프로세스는 Thread 유형의 변수를 선언하고 run 메서드를 재정의하는 것입니다. 샘플 코드는 다음과 같습니다.
Thread에서 클래스를 파생시키고 위와 비슷한 방식으로 run 메서드를 재정의할 수 있습니다. 샘플 코드는 다음과 같습니다.
공개 정적 무효 createThreadBySubClass()
{
MyThread 스레드 = 새로운 MyThread();
thread.start();
}
Runnable 인터페이스를 구현하는 클래스를 정의한 다음 이 클래스의 인스턴스를 매개변수로 사용하여 Thread 변수 생성자를 구축할 수 있습니다. 샘플 코드는 다음과 같습니다.
공개 정적 무효 createThreadByRunnable()
{
MyRunnable 실행 가능 = 새로운 MyRunnable();
스레드 스레드 = 새 스레드(실행 가능);
thread.start();
}
여기에는 Java의 멀티스레딩 실행 모드가 포함됩니다. Java의 경우 멀티스레딩이 실행 중일 때 "다중 개체 멀티스레딩"과 "단일 개체 멀티스레딩" 간에 차이가 있습니다.
다중 객체 다중 스레딩 , 프로그램은 실행 중에 여러 스레드 객체를 생성하고 각 객체에서 하나의 스레드가 실행됩니다.
단일 객체 다중 스레딩 , 프로그램은 실행 중에 스레드 객체를 생성하고 그 위에서 여러 스레드를 실행합니다.
분명히 스레드 동기화 및 예약 관점에서 보면 다중 개체 다중 스레딩이 더 간단합니다. 위의 세 가지 스레드 생성 방법 중 처음 두 가지는 "다중 객체 다중 스레드"이고, 세 번째는 "다중 객체 다중 스레드" 또는 "단일 객체 단일 스레드"를 사용할 수 있습니다.
Object.notify 메서드를 사용하는 다음 샘플 코드를 살펴보겠습니다. 이 메서드는 개체의 스레드를 깨우고 Object.notifyAll 메서드는 개체의 모든 스레드를 깨웁니다.
public static void main(String[] args)에서 InterruptedException이 발생합니다.
{
통지테스트();
통지테스트2();
통지테스트3();
}
개인 정적 무효 통지 테스트()가 InterruptedException을 발생시킵니다.
{
MyThread[] arrThreads = 새로운 MyThread[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = 새로운 MyThread();
arrThreads[i].id = i;
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
for (int i = 0; i < arrThreads.length; i++)
{
동기화됨(arrThreads[i])
{
arrThreads[i].notify();
}
}
}
private static void informTest2()가 InterruptedException을 발생시킵니다.
{
MyRunner[] arrMyRunners = 새로운 MyRunner[3];
Thread[] arrThreads = 새 스레드[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrMyRunners[i] = 새로운 MyRunner();
arrMyRunners[i].id = i;
arrThreads[i] = new Thread(arrMyRunners[i]);
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
for (int i = 0; i < arrMyRunners.length; i++)
{
동기화됨(arrMyRunners[i])
{
arrMyRunners[i].notify();
}
}
}
private static void informTest3()에서 InterruptedException이 발생합니다.
{
MyRunner 러너 = new MyRunner();
Thread[] arrThreads = 새 스레드[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = 새 스레드(러너);
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
동기화(러너)
{
러너.notifyAll();
}
}
}
MyThread 클래스는 Thread를 확장합니다.
{
공개 int ID = 0;
공개 무효 실행()
{
System.out.println("Thread " + id + "는 5분 동안 절전 모드로 전환될 준비가 되어 있습니다.");
노력하다
{
동기화됨(이것)
{
this.wait(5*60*1000);
}
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println(""th" + id + "스레드가 활성화되었습니다.");
}
}
MyRunner 클래스는 Runnable을 구현합니다.
{
공개 int ID = 0;
공개 무효 실행()
{
System.out.println("Thread " + id + "는 5분 동안 절전 모드로 전환될 준비가 되어 있습니다.");
노력하다
{
동기화됨(이것)
{
this.wait(5*60*1000);
}
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println(""th" + id + "스레드가 활성화되었습니다.");
}
}
informAll 메소드는 "단일 객체 다중 스레드" 시나리오에 적합합니다. 왜냐하면 통지 메소드는 객체에서 하나의 스레드만 무작위로 깨우기 때문입니다.
스레드 상태 전환
스레드의 경우 스레드를 생성한 시점부터 스레드가 종료될 때까지 이 프로세스 중 스레드의 상태는 다음과 같을 수 있습니다.
생성: 이미 Thread 인스턴스가 있지만 CPU에는 여전히 리소스와 시간 조각이 할당되어 있습니다.
준비(Ready): 스레드가 실행에 필요한 모든 리소스를 획득했으며 CPU가 시간을 예약하기를 기다리고 있습니다.
실행 중: 스레드가 현재 CPU 시간 조각에 위치하며 관련 논리를 실행 중입니다.
Sleep: 일반적으로 Thread.sleep을 호출한 후의 상태입니다. 이 때 스레드는 여전히 작업에 필요한 다양한 리소스를 보유하고 있지만 CPU에 의해 예약되지는 않습니다.
Suspend: 일반적으로 Thread.suspension을 호출한 후의 상태입니다. CPU는 스레드를 예약하지 않습니다. 차이점은 이 상태에서는 스레드가 모든 리소스를 해제한다는 것입니다.
사망: 스레드가 종료되거나 Thread.stop 메서드가 호출됩니다.
다음으로 스레드 상태를 전환하는 방법을 보여드리겠습니다. 먼저 다음 방법을 사용하겠습니다.
Thread() 또는 Thread(Runnable): 스레드를 구성합니다.
Thread.start: 스레드를 시작합니다.
Thread.sleep: 스레드를 절전 상태로 전환합니다.
Thread.interrupt: 스레드 실행을 중단합니다.
Thread.join: 스레드가 끝날 때까지 기다립니다.
Thread.yield: CPU에서 스레드의 실행 시간 조각을 박탈하고 다음 예약을 기다립니다.
Object.wait: 객체의 모든 스레드를 잠그고 알림 메소드가 실행될 때까지 계속 실행되지 않습니다.
Object.notify: 객체의 스레드를 무작위로 깨웁니다.
Object.notifyAll: Object의 모든 스레드를 깨웁니다.
이제 시연 시간입니다! ! !
스레드 대기 및 깨우기
여기서는 Object.wait 및 Object.notify 메소드가 주로 사용됩니다. 위의 알림 인스턴스를 참조하세요. Runnable 인터페이스를 구현하여 스레드를 생성할 때 Wait와 통지는 모두 Thread 개체 대신 Runnable 개체에 대해 이 두 가지 메서드를 사용해야 합니다.
스레드 잠자기 및 깨어남
public static void main(String[] args)에서 InterruptedException이 발생합니다.
{
수면 테스트();
}
개인 정적 무효 sleepTest()가 InterruptedException을 발생시킵니다.
{
스레드 스레드 = 새 스레드()
{
공개 무효 실행()
{
System.out.println("Thread" + Thread.currentThread().getName() + "5분 동안 절전 모드로 전환됩니다.");
노력하다
{
Thread.sleep(5*60*1000);
}
catch(InterruptedException ex)
{
System.out.println("Thread" + Thread.currentThread().getName() + "Sleep이 중단되었습니다.");
}
System.out.println("스레드" + Thread.currentThread().getName() + "슬립이 종료되었습니다.");
}
};
thread.setDaemon(true);
thread.start();
Thread.sleep(500);
스레드.인터럽트();
}
}
Thread.stop 메소드가 있지만 이 메소드는 권장되지 않습니다. IterruptedException을 처리할 때 스레드가 스레드를 종료하도록 위의 절전 및 깨우기 메커니즘을 사용할 수 있습니다.
public static void main(String[] args)에서 InterruptedException이 발생합니다.
{
중지테스트();
}
개인 정적 무효 stopTest()가 InterruptedException을 발생시킵니다.
{
스레드 스레드 = 새 스레드()
{
공개 무효 실행()
{
System.out.println("스레드가 실행 중입니다.");
노력하다
{
Thread.sleep(1*60*1000);
}
catch(InterruptedException ex)
{
System.out.println("스레드 인터럽트, 스레드 종료");
반품;
}
System.out.println("스레드가 정상적으로 종료되었습니다.");
}
};
thread.start();
Thread.sleep(500);
스레드.인터럽트();
}
}
메인 스레드에서 10개의 하위 스레드를 생성하고 10개의 하위 스레드가 모두 완료된 후 메인 스레드가 다음 로직을 실행할 것으로 예상하면 이제 Thread.join이 나타날 때입니다.
public static void main(String[] args)에서 InterruptedException이 발생합니다.
{
JoinTest();
}
개인 정적 무효 JoinTest()가 InterruptedException을 발생시킵니다.
{
스레드 스레드 = 새 스레드()
{
공개 무효 실행()
{
노력하다
{
for(int i = 0; i < 5; i++)
{
System.out.println("스레드가 실행 중입니다.");
Thread.sleep(1000);
}
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
};
thread.setDaemon(true);
thread.start();
Thread.sleep(1000);
thread.join();
System.out.println("메인 스레드가 정상적으로 종료되었습니다.");
}
}
프로세스 아래의 모든 스레드는 메모리 공간을 공유한다는 것을 알고 있습니다. 그렇다면 서로 다른 스레드 간에 메시지를 어떻게 전송합니까? Java I/O를 검토할 때 PipedStream 및 PipedReader에 대해 이야기했으며 여기에서 이들이 작동하게 됩니다.
다음 두 예제는 정확히 동일한 기능을 가지고 있는데, 하나는 Stream을 사용하고 다른 하나는 Reader/Writer를 사용한다는 점입니다.
스레드 thread1 = 새 스레드()
{
공개 무효 실행()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
노력하다
{
동안(사실)
{
문자열 메시지 = br.readLine();
pos.write(message.getBytes());
if (message.equals("end")) break;
}
br.닫기();
pos.close();
}
catch(예외예외)
{
ex.printStackTrace();
}
}
};
스레드 thread2 = 새 스레드()
{
공개 무효 실행()
{
바이트[] 버퍼 = 새 바이트[1024];
int bytesRead = 0;
노력하다
{
while((bytesRead = pis.read(buffer, 0, buffer.length)) != -1)
{
System.out.println(새 문자열(버퍼));
if (new String(buffer).equals("end")) break;
버퍼 = 널;
버퍼 = 새 바이트[1024];
}
pis.close();
버퍼 = 널;
}
catch(예외예외)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(true);
thread2.setDaemon(true);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
스레드 thread1 = 새 스레드()
{
공개 무효 실행()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
노력하다
{
동안(사실)
{
문자열 메시지 = br.readLine();
bw.write(메시지);
bw.newLine();
bw.플러시();
if (message.equals("end")) break;
}
br.닫기();
pw.close();
bw.close();
}
catch(예외예외)
{
ex.printStackTrace();
}
}
};
스레드 thread2 = 새 스레드()
{
공개 무효 실행()
{
문자열 라인 = null;
노력하다
{
while((line = br.readLine()) != null)
{
System.out.println(line);
if (line.equals("end")) break;
}
br.닫기();
pr.close();
}
catch(예외예외)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(true);
thread2.setDaemon(true);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}