애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션이 실행되는데, 이것을 프로세스(process)라고 한다. 그리고 프로세스 내부에서 코드의 실행 흐름을 스레드(thread)라고 한다.
운영체제에서는 실행 중인 하나의 애플리케이션을 프로세스라고 부른다. 사용자가 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는데 이것이 프로세스이다.
하나의 애플리케이션은 멀티 프로세스를 만들기도 한다.
운영체제는 두 가지 이상의 작업을 동시에 처리하는 멀티 태스킹을 할 수 있도록 CPU 및 메모리 자원을 프로세스마다 적절히 할당해주고, 병렬로 실행시킨다. 예를 들어, 워드로 문서 작업을 하면서 동시에 윈도우 미디어 플레이어로 음악을 들을 수 있다.
멀티 태스킹은 꼭 멀티 프로세스를 뜻하는것은 아니다. 한 프로세스 내에서 멀티 태스킹을 할 수 있도록 만들어진 애플리케이션도 있다. 대표적인 것이 미디어 플레이어와 메신저이다. 미디어 플레이어는 동영상 재생과 음악 재생이라는 두가지 작업을 동시에 처리하고, 메신저는 채팅 기능을 제공하면서 동시에 파일 전송 기능을 수행하기도 한다.
멀티 프로세스는 자신의 메모리를 가지고 실행하므로 서로 독립적이지만, 멀티 스레드는 하나의 프로세스 내부에 생성되므로 스레드 하나가 예외를 발생시키면 다른 스레드도 영향을 받는다.
스레드는 사전적 의미로 한 가닥의 실이라는 뜻인데, 한 가지 작업을 실행하기 위해 순차적으로 실행할 코드를 실처럼 이어놓았다고 해서 유래된 이름이다. 하나의 스레드는 하나의 코드 실행 흐름이기 때문에 한 프로세스 내에 스레드가 2개라면 2개의 코드 실행 흐름이 생긴다는 의미이다.
멀티 태스킹(multi-tasking) : 컴퓨터에서 여러가지 일을 동시에 처리하는 것
프로세스 기반의 멀티 태스킹 : 프로그램이 여러 개 동시에 수행되면서 여러 일을 동시에 진행
스레드 기반의 멀티태스킹 – 하나의 프로그램 내에서 여러 개의 스레드가 동시에
수행되면서 여러 일을 동시에 진행.
현재 수행되고 있는 프로그램이 프로세스, 스레드는 작은 프로세스.
main 함수를 main 스레드라고도 함.
자바의 모든 애플리케이션은 메인 스레드가 main()메소드를 실행하면서 시작한다. 메인 스레드는 main()메소드의 첫 코드부터 아래로 순차적으로 실행하고, main()메소드의 마지막 코드를 실행하거나 return문을 만나면 실행이 종료된다.
멀티 프로세스는 운영체제에서 할당받은 자신의 메모리를 가지고 실행하기 대문에 각 프로세스는 서로 독립적이다. 다라서 하나의 프로세스에서 오류가 발생해도 다른 프로세스에 영향을 미치지않는다. 하지만 멀티 스레드는 하나의 프로세스 내부에 생성되기 때문에 하나의 스레드가 예외를 발생시키면 프로세스 자체가 종료될 수 있어 다른 스레드에 영향을 미치게 된다. 그렇기 때문에 멀티 스레드에서는 예외 처리에 만전을 기해야 한다.
자바에서 쓰레드를 사용하는 이유는 동시에 두가지 이상의 활동을 하기 위함이다.
그리고 프로세스끼리는 정보를 주고 받을 수는 없지만, 다중 쓰레드 작업시에 각 쓰레드끼리는 정보를 주고 받을 수 있는 장점이 있다.
자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필ㅇ하다. java.lang.Thread 클래스를 직접 객체화해서 생성해도 되지만, Thread 클래스를 상속해서 하위 클래스를 만들어 생성할 수도 있다.
핵심 포인트
프로세스 : 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션이 실행되는데, 이것을 프로세스라고 한다.
멀티 스레드 : 하나의 프로세스 내에 동시 실행을 하는 스레드들이 2개 이상인 경우를 말한다.
메인 스레드 : 자바의 모든 애플리케이션은 메인 스레드가 main()메소드를 실행하면서 시작한다. 메인 스레드는 main()메소드의 첫 코드부터 아래로 순차적으로 실행하고, main()메소드의 마지막 코드를 실행하거나 return문을 만나면 실행이 종료된다.
작업 스레드 : 메인 작업 이외에 병렬 작업의 수만큼 생성하는 스레드를 말한다. 작업 스레드도 객체로 생성되기 때문에 클래스가 필요하다. Thread클래스를 직접 객체화해서 생성할 수도 있고, Thread클래스를 상속해서 하위 클래스를 만들어 생성할 수도 있다.
스레드 상태 : 스레드를 생성하고 시작하면 스레드는 다양한 상태를 가지게 된다. 스레드의 상태는 자동으로 변경될 수도 있고, 코드에 의해서 변경될 수도 있다.
일시정지 : 실행중인 스레드를 일정시간 멈추게 하고 싶다면 Thread 클래스의 정적 메소드인 sleep()을 사용하면 된다. 다음과 같이 Thread.sleep()메소드를 호출한 스레드는 주어진 시간동안 일시정지 상태가 되고, 다시 실행 대기 상태로 돌아간다.
Toolkit
그래픽 자원 관련 시스템 정보를 제공하거나 화면의 해상도를 얻어내거나, 신호음을 출력하는 등의 메소드를 가지고 있다. 운영체제의 정보를 직접 조회하거나 사용하는데 활용한다.
Toolkit객체를 얻어서 사용하려면 new 명령으로 만들 수 없으며 Toolkit 객체가 하나만 존재해야 하기 때문에 Toolkit.getDefaultToolkit()을 사용해서 객체를 얻는다.
Runnable 인터페이스
Runnable은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체라고 해서 붙여진 이름으로 인터페이스 타입이기 때문에 구현 객체를 만들어 대입하며 Runnable에는 run()메소드 하나가 정의되어 있는데 구현 클래스는 run()을 재정의해서 작업 스레드가 실행할 코드를 작성해야 한다.
작업스레드는 생성되는 즉시 실행되는것이 아니라 start()를 호출해야만 실행된다. start()를 호출하면 run()메소드가 실행되는 것이다.
코드를 좀 더 간단하게 작성하기 위해서 Thread생성자를 호출할 때 Runnable 익명객체를 매개값으로 사용하는 방법이 더 많이 사용된다.
작업 스레드가 실행할 작업을 Runnable 인터페이스를 만들지 않고 Thread의 하위 클래스로 작업 스레드를 정의하면서 작업 내용을 포함시킬 수도 있다. Thread클래스를 상속한 후 run()메소드를 재정의해서 스레드가 실행할 코드를 작성하면 된다. 작업 스레드 클래스로부터 작업 스레드 객체를 생성하는 방법은 일반적인 객체를 생성하는 방법과 동일하다.
작업 스레드가 실행할 작업을 Runnable 인터페이스 구현 후 하나의 파일안에서 실행 클래스의 메인 메소드를 실행하여 멀티 스레드로 생성과 실행이 가능하다. Thread클래스를 상속받아서 만든 스레드를 수행하는 방법과 Runnable 인터페이스 구현을 이용하는 스레드를 수행하는 방법 모두 수행 결과에서는 차이가 없다. 다만 Thread클래스를 상속받는 경우에는 다른 클래스의 다중상속이 불가능하기 때문에 Runnable 인터페이스를 구현하면서 다른 클래스도 상속받아야 하는 경우라면 Runnable을 이용한다.
스레드는 자신의 이름을 가지고 있다. 디버깅할 때 어떤 스레드가 어떤 작업을 하는지 조사할 목적으로 가끔 사용된다. 메인 스레드는 "main"이라는 이름을 가지고 있고, 우리가 직접 생성한 스레드는 자동적으로 "Thread-n"이라는 이름으로 설정된다. n은 스레드의 번호를 말한다. Thread-n 이름 말고 다른 원하는 이름으로 설정하고 싶다면 Thread클래스의 setName()메소드로 변경 가능하고 현재 스레드의 이름을 알고 싶다면 getName()메소드를 호출하면 된다. 만약 스레드 객체의 인스턴스를 가지고 있지 않다면 Thread의 정적 메소드인 .currentThread()로 코드를 실행하는 현재 스레드의 이름을 얻을 수 있다.
스레드의 개수가 코어의 수보다 많을 경우, 스레드를 어떤 순서에 의해서 번갈아가면서 실행할것인지(동시성)를 결정해야 하는데 이것을 스레드 스케줄링이라고 하며 이에 의해 스레드들은 아주 짧은 시간동안 번갈아가면서 그들의 run()메소드를 조금씩 실행한다.
자바의 스레드 스케줄링은 우선순위 방식(우선순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케줄링 하는것), 순환 할당 방식(시간 할당량을 정해서 하나의 스레드를 정해진 시간만큼 실행하고 다시 다른 스레드를 실행하는 방식)이 있다.
우선순위 방식은 개발자가 스레드 객체에 우선순위를 부여할 수 있기 때문에 코드로 제어가 가능하고 순환 할당 방식은 JVM에 의해서 결정되기 때문에 코드로 제어가 불가능하다.
-우선순위방식은 1~10까지 부여되는데 1이 우선순위가 낮고 10이 가장 높다. 우선순위를 부여하지 않으면 기본적으로 5를 할당받는데 우선순위를 변경하고 싶다면 thread.setPriority(우선순위 상수)를 이용하면 된다.
-스레드가 수행되는 데에는 우선순위 뿐만이 아니라 현재 시스템 환경도 영향을 주기 때문에 우선순위를 항상 보장하지는 않지만 평균적으로 적용된다.
join():스레드가 끝날때까지 기다림
스레드가 모두 종료되기 전에 main메소드가 먼저 종료될 수도 있고, 맨 마지막에 종료될 수도 있고 메인스레드와 작업스레드가 종료되는 시점은 시스템 환경에 따라서 다르다.
멀티 스레드 프로그램에서는 여러 스레드들이 객체를 공유해서 작업하는 경우가 있다. 이 때 여러 스레드에서 동시에 한 객체에 접근할 수 있게 되면, 한 스레드에 의해 변경된 객체의 상태가 다른 스레드의 작업에 영향을 미쳐서 의도치 않은 결과를 만들어 낼 수 있는데 이런 상황을 방지하기 위해 한 스레드가 사용중인 객체를 다른 스레드에서 동시에 접근할 수 없도록 잠금으 걸어줄 수 있는 기능이 바로 동기화(synchronized)이다.
자바는 임계 영역(Critical section)을 지정하기 위해 동기화 메소드와 동기화 블록을 제공한다. 임계 영역이란 멀티 스레드 프로그램에서 하나의 스레드 이외에는 동시에 실행할 수 없는 코드 영역을 말한다. 스레드가 동기화 메소드 및 동기화 블록에 들어가면 즉시 객체에 잠금을 걸어 다른 스레드가 임계 영역의 코드를 실행하지 못하도록 한다. 스레드에 의해 걸린 객체의 잠금은 스레드가 동기화 메소드를 종료하는 즉시 해제된다.
1)동기화 메소드 선언시 synchronized 키워드를 붙여서 만들 수 있다.
public synchronized void method(){
...임계영역
}
2)메소드의 일부 영역만 임계 영역으로 만들고 싶다면 동기화 블록을 만들면 된다.
public void method(){
//여러 스레드가 실행 가능한 영역
synchronized(공유객체){
//임계영역
}
자바는 임계 영역을 지정하기 위해 메소드 선언의 리턴타입 앞에 synchronized 키워드를 붙이면 동기화 메소드가 된다. 이 키워드는 인스턴스와 정적 메소드 어디든 붙일 수 있다.
'Java > information' 카테고리의 다른 글
[자바/JAVA] annotation (0) | 2023.10.15 |
---|---|
[자바/JAVA] 람다식과 열거형 (0) | 2023.06.13 |
[자바/JAVA] 컬렉션 프레임워크 (0) | 2023.06.09 |
[자바/JAVA] Iterator와 ListIterator (0) | 2023.06.08 |
[자바/JAVA] 제네릭 컬렉션 프레임워크 (0) | 2023.06.03 |