Java에서의스레드운영체제 - 스레드
。Java는스레드기반으로 개발한 언어
▶자바로 구축한Application은 기본적으로스레드기반으로 실행하며메인 스레드에서main()를 실행하여 시작
。기본적으로Java 프로그램은메인 스레드( =main()) 1개로 실행하지만,멀티스레딩을 지원
▶Thread / Runnable사용 시 여러 작업을Concurrently하게 실행
▶main()의메인스레드만 실행 시 만약Thread.sleep(5000)을 설정하는 경우 해당sleep 기간동안 다른 작업의 수행이 불가능.
。여러종류의스레드 API를 지원
▶ 각스레드의 생성 및 관리가 용이
。멀티스레드:물리적 스택을 늘리는 것
▶new Thread():메모리상에스레드가 생성되어CPU를 할당받고 실행
。JAVA는User Thread를 생성 및 관리
▶OS에 의해 관리되지 않으며JAVA에 의해스레드가 관리됨
▶JVM에 의해 특정OS의kernel thread(pthreads등 )와Many to One Model등의 관계를 맺어서 운용
JAVA에서스레드를 생성하는 방법
。java.lang.runnable,java.lang.thread,ExecutorService,ThreadPool,@Async활용
▶ 모두Runnable기반으로 발전
java.lang
스레드 생명주기
NEW:
。스레드 객체가 생성됬지만,start()가 호출되지 않은 상태
RUNNABLE:
。CPU 할당을 대기하면서 실행 가능한상태
RUNNING:
。스레드가CPU를 점유하여 실제로 작업을 처리중인상태
BLOCKED:
。스레드가synchronized 키워드 블록의 진입을 대기하는 상태
WAITING:
。스레드가join()에 의해 다른스레드의 작업 완료를 대기하는 상태
TIMED_WAITING
。스레드가sleep()에 의해 일정 시간 대기중인 상태
TERMINATED
。스레드의 실행이 완료된 상태 실행이 완료된 상태
Thread:java.lang.thread
。자바 프로그램에서멀티스레딩을 위해 사용하는클래스
▶비동기 작업에서 중요하게 사용
。메인스레드(=main())에서Thread 클래스객체를 활용하여 여러작업을 동시에 처리 가능
。Thread를 객체로서 직접 생성 및 제어하는 기능이 존재
▶ 해당Class를 상속한Class 객체를스레드로서 조작
。단.JAVA의 Class는다중상속이 안되는 특징이 존재하므로 보통Runnable Interface를 구현하는 방식을 사용
▶Thread를 상속하는클래스에서 다른클래스를 상속할 수 있음
Thread 메서드/Runnable 메서드
스레드객체.start()
。해당스레드객체를 실행하여 새로운스레드를 생성 및run()를 호출
run()
。해당스레드가 실행할 작업을 정의하는메서드
▶Thread또는Runnable을 상속한Class에 해당 메소드를Override하여 작업을 정의
。start()에 의해 생성된스레드에서 호출
▶메인스레드에서 직접run()을 호출하는 경우일반 메서드 호출
스레드객체.sleep(ms)
。지정한milisec만큼 현재스레드를 일시중지
스레드객체.join()
。스레드실행을Block하여 다른스레드가 종료될 때까지 대기
▶부모스레드에서자식스레드객체.join()을 실행 시자식스레드의작업이 모두 끝날 때까지 대기
。프로세스의System Call : wait()과 동일한 역할
스레드객체.currentThread()
。현재 실행중인스레드 객체를 반환
스레드객체.interrupt()
。현재 실행중인스레드에서인터럽트(InterruptedException)를 발생시켜서스레드를Termination
▶스레드객체.stop()의 경우 동기화문제로 deprecated.public class StreamApi { public static void main(String[] args) throws InterruptedException { ThreadByClass t = new ThreadByClass("영상.mp4 "); t.start(); // 스레드 생성 및 run() 실행 t.join(); // t 스레드가 끝날 때 까지 main 스레드 대기 t.interrupt(); // t 스레드에 인터럽트예외를 발생시켜서 종료 } }
Thread 클래스를 활용하여스레드생성
Thread Class를상속한스레드 클래스를 생성
。임의의Java Class를 생성 및Thread Class를 상속 후public void run()메소드를오버라이딩
。이후메인스레드(main())에서 해당Java Class의 객체를 생성 후Thread Class의start()를 호출하여 해당스레드로Context Switching
▶run()을 직접 호출하면 안되며,Thread Class의start()를 호출하여Java Class에Override한run()을 실행해야한다.class ThreadByClass extends Thread { private final String file; public ThreadByClass(String file) { this.file = file; } // Thread Class에서 메소드 오버라이딩 @Override public void run(){ try { while(true){ System.out.println(file + "스레드 실행중"); Thread.sleep(500); } } catch(InterruptedException e){ e.printStackTrace(); } } } public class StreamApi { public static void main(String[] args) { ThreadByClass t1 = new ThreadByClass("영상.mp4 "); ThreadByClass t2 = new ThreadByClass("음악.mp3 "); ThreadByClass t3 = new ThreadByClass("문서.hwp "); // 메인스레드( main() )에서 다른 스레드( t1 )으로 Context Switch 발생 t1.start(); // run() 구동 t2.start(); t3.start(); // Context Switch 이 완료되기전이므로 우선 실행 System.out.println("메인스레드 : 다운로드 중에도 다른 작업 가능"); } }MyThread t = new MyThread(); // t.run(); // 그냥 메서드 호출 → 새 스레드 생성 안 됨, 메인 스레드에서 실행됨 t.start(); // 새 스레드를 생성하고 그 스레드에서 run()을 실행함▶
스레드객체.start()를 통해 호출되며,run()을 직접 호출 시 그냥메서드 호출로서메인스레드에서 실행
。메인스레드에서main()메소드를 실행 시ThreadByClass객체 생성 및thread.start()를 통해 해당스레드로Context Switch가 완료되기 전Non-Blocking으로System.out.println("Thread started");이 먼저 출력
。메인스레드로부터Context Switch가 완료된 경우ThreadByClass에서Method Overriding한run()에 구현된 코드를 실행
Runnable:java.lang.runnable
。스레드가 실행할작업( =run())을 정의하는인터페이스
▶ 실무에서스레드생성 시Thread 클래스를 대신해서 주로 활용
。Runnable 인터페이스를 구현한Java Class 객체를 생성자로 전달하여Thread Class 객체생성 후 활용
Thread thread = new Thread(new Runnable구현클래스())
Runnable을 활용하는 이유
상속 제한해결
。Thread 클래스는단일 상속의 문제가 존재하므로,상속이 제한되어Runnable 인터페이스를 통해다중 상속class A extends SomeClass implements Runnable
역할 분리
。Thread 클래스는작업 내용을 정의하면서 동시에실행 주체로서 사용되지만,Runnable 인터페이스는 내부에작업 내용만 정의하여 활용가능
▶작업(=Runnable)과스레드(=Thread)를 분리하여재사용성이 높음.
Runnable Interface를구현 클래스를 정의하여스레드생성
。가장 많이 활용하는 방식
。임의의Java Class에서Runnable Interface를 구현 후 부모 Class의public void run()메소드를오버라이딩
。 해당구현체를Thread Class의생성자로 전달하여스레드생성 및start()를 호출하여Context Switchingclass ThreadByInterface implements Runnable{ // Thread Class에서 method override public void run(){ try{ while(true){ System.out.println("Thread"); Thread.sleep(500); // 0.5초 간격 } } // 스레드 종료 관측 시 catch(InterruptedException ie){ System.out.println("interrupted"); } } } public class MyThread { // 메인스레드 실행 public static void main(String[] args) throws Exception { // ThreadByInterface객체를 활용해 Thread 객체 생성 Thread thread = new Thread(new ThreadByInterface()); thread.start(); // 메인스레드에서 다른 스레드로 Context Switch 발생 System.out.println("Thread started"); // Context Switch 이 완료되기전이므로 우선 실행 } }
익명 스레드:람다표현식( Lambda Expression )을 활용하여Runnable을 통한스레드생성
。JAVA 1.8부터 사용가능
。람다 표현식을 통해Runnable 구현체생성 후Thread Class생성자로 전달하여스레드 객체생성 및start()를 호출하여Context Switching
▶람다 표현식을 통해Runnable을상속하는클래스를 정의할 필요가 없다.
。 임의의Java Class를 선언하는 방식보다 간결하게 표현 가능public class MyThread { // 메인스레드 실행 public static void main(String[] args) throws Exception { Runnable task = () -> { try{ while(true){ System.out.println("Thread"); Thread.sleep(500); // 0.5초 간격 } } // 스레드 종료 관측 시 catch(InterruptedException ie){ System.out.println("interrupted"); } }; Thread thread = new Thread(task); thread.start(); // 메인스레드에서 다른 스레드로 Context Switch 발생 System.out.println("Thread started"); // Context Switch 이 완료되기전이므로 우선 실행 } }
Java에서Non-Blocking을 방지하기위해부모 스레드의 실행을 대기시키는 방법
。스레드객체.join()을 통해 해당스레드가 실행이 완료될때까지부모스레드를Block하여 대기.
▶프로세스에서fork()를 통해자식프로세스생성 시부모 프로세스의System Call : wait()과 같은 개념
。부모 스레드의Non-Blocking을 통한 동시처리 방지
▶자식스레드의 모든 실행이 끝나면메인스레드실행 재개public class MyThread { // 메인스레드 실행 public static void main(String[] args) throws Exception { // 자식스레드가 실행할 코드 정의 System.out.println("메인스레드 실행"); Runnable task = () -> { System.out.println("자식스레드 실행"); }; Thread thread = new Thread(task); thread.start(); // 메인스레드에서 다른 스레드로 Context Switch 발생 try{ System.out.println("메인스레드 실행 Block"); thread.join(); System.out.println("자식스레드 실행완료 / 메인스레드 실행 재개"); } // 스레드 종료 관측 시 catch(InterruptedException ie){ System.out.println("interrupted"); } // Non-Blocking으로 자식스레드 모두 실행 시 메인스레드 실행 System.out.println("부모스레드 실행완료"); } }
。스레드객체.join()을 통해 해당스레드작업이 완료될때까지부모스레드(=메인스레드) 의 실행을Block
Java에서 실행중인스레드를Termination하는 방법
。스레드객체.interrupt()를 통해인터럽트를 발생시켜서스레드를 종료public class MyThread { // 메인스레드 실행 public static void main(String[] args) throws Exception { Runnable task = () -> { try{ while(true){ System.out.println("Thread"); Thread.sleep(500); // 0.5초 간격으로 반복 } } // 스레드 종료 관측 시 catch(InterruptedException ie){ System.out.println("interrupted"); } }; Thread thread = new Thread(task); thread.start(); // 메인스레드에서 다른 스레드로 Context Switch 발생 Thread.sleep(2000); thread.interrupt(); // 2초후 스레드 객체에 인터럽트 발생 System.out.println("메인스레드 실행완료"); } }