본문 바로가기

study/java

자바/JAVA 스레드(1)

프로세스(process): OS에서 실행 중인 하나의 애플리케이션이다. OS로부터 메모리를 할당받아 코드를 실행한다. 하나의 애플리케이션은 멀티 프로세스를 만들기도 한다. -> 멀티 프로세서의 예) 미디어 플레이어는 동영상 재생과 음악 재생을 동시에 한다.

 

OS는 멀티 태스킹할 수 있도록, 메모리를 프로세스마다 할당하고 병렬로 실행시킨다.

 

멀티 태스킹은 OS가 두 가지 이상의 작업을 처리하는 것인데, 이것은 멀티 프로세스와 다르다. 미디어 플레이어는 하나지만 멀티 프로레스이다.

 

스레드(thread): 프로세스 내에서 실행되는 하나의 코드 실행(작업) 흐름 단위이다. 하나의 프로세스는 하나 이상의 스레드를 갖는다.

 

멀티 스레드: 하나의 프로세스 내에 동시 실행을 하는 스레드들이 2개 이상인 경우.

 

멀티 프로세스에선 각 프로세스가 각자의 메모리를 갖고 실행하기 때문에 독립적이다. 그러나 멀티 스레드는 하나의 프로세스 내부에 생성되기 때문에 한 스레드에서 예외가 발생되면 다른 스레드에 영향을 미친다.

 

예를 들어 엑셀과 워드를 사용할 때, 워드에 에러가 발생해도 엑셀은 잘 작동되지만(멀티 프로세스), 채팅 프로그램 사용 중 전송 기능 스레드에서 예외가 발생하면 채팅 프로세스 자체가 종료된다.

 

 

자바의 메인 스레드는 main() 메소드를 실행하면서 시작한다. main() 메소드가 종료되면 메인 스레드도 종료된다.

 

작업 스레드: 메인 작업 이외의 병렬 작업의 수만큼 생성하는 스레드.

 

멀티 스레드에선 여러 스레드가 작동되므로 메인 스레드 뿐만 아니라 다른 스레드들까지 끝나야 프로세스가 종료된다.

 

마찬가지로 메인 스레드가 다른 스레드의 start() 메소드를 실행하면, 그 스레드의 run() 메소드를 실행시키고 다시 메인 스레드의 작업을 실행한다.

 

프로세스와 스레드의 관계

 

자바에서 메인 스레드(main()) 외의 스레드를 생성하려면 java.lang.Thread 클래스를 객체화하거나 상속받는 하위 클래스를 만들어 객체화해서 스레드를 생성한다.

 

 

java.lang.Thread로 직접 생성하려면 Runnable 객체를 매개값으로 갖는 생성자를 호출해야 한다.

Runnable은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체이다. 인터페이스이기 때문에 구현 객체를 만들어 생성자에 대입해야 하고 run() 메소드를 재정의해야 한다.

run() 메소드 안에는 스레드가 실행할 코드를 적는다.

 

Thread thread = new Thread(Runnable target);

////////

class target implements Runnable{
	@override
    run(){
    	System.out.println("HI"); //스레드가 실행할 코드
        }
    }

 

익명 구현 객체를 사용하면 코드를 절약할 수 있다.

 

생성된 스레드는 즉시 시작되는 것이 아니라, 스레드의 start() 메소드를 실행시켜야 한다.

start() 메서드가 호출되면 매개 값으로 받은 Runnable의 run() 메서드를 실행시키고, 다시 자신의 작업을 처리한다.

 

Thread thread = new Thread(new Runnable(){
	@override
    run(){
    	System.out.println("HI"):
        }
});

thread.start(); // 전달받은 Runnable의 run() 메소드를 실행시키고 다시 자신의 작업을 처리한다.

 

 

Thread의 하위 클래스를 이용해 스레드를 생성하려면 Thread 클래스를 상속받고, run()메소드를 재정의하면 된다. 이렇게 하면 Runnable이 없어도 작업 내용을 포함시킬 수 있다.

 

// Thread 클래스 상속 받아 하위 클래스 만들기
public class myThread extends Thread {
	@override
	run(){
		Systme.out.println("HI"); //스레드가 실행할 코드
	}
}

// 익명 자식 객체 사용

Thread thread = new Thread() {
	@override
	run(){
		Systme.out.println("HI"); //스레드가 실행할 코드
	}	
}

thread.start();

 

 

스레드는 이름을 갖는다.(참조 말고) 디버깅할 때 조사 목적으로 사용된다. 메인 스레드는 'main'이란 이름을 갖고 있고, 직접 생성한 스레드는 자동으로 'Thread-n'으로 이름이 설정된다. n은 스레드의 번호다.

 

setName("이름") 메소드로 이름을 설정할 수 있고 getName() 메소드로 이름을 얻을 수도 있다. 이 메소드들은 인스턴스 메소드이므로 참조 변수가 있을 때 사용할 수 있다.

 

Thread 클래스의 정적 메소드인 currentThread()를 이용하면 현재 스레드의 참조를 얻을 수 있다.

Thread thread = Thread.currentThread();

 

toString() 메소드를 호출하면 스레드의 이름, 우선순위, 속한 그룹 이 반환된다.

스레드 우선 순위 : Thread.MIN_PRIORITY(=1) ~ Thread.MAX_PRIORITY(=10). 우선 순위가 높은 Thread는 cpu를 배분 받을 확률 높음.
디폴트 우선 순위 : Thread.NORM_PRIORITY(=5)
우선 순위 관련 메소드
setPriority(int newPriority), int getPriority()

 

멀티 스레드 프로그램은 스레드들이 객체를 공유해서 작업하는 경우가 있다. 여기서 주의할 점이 있다.

만약 스레드A가 사용하던 객체를 스레드B가 변경했다면, 잘못된 결과가 나올 수 있다.

 

예를 들어, 사용자1이 계산기의 메모리를 100으로 설정하고 10초 후 출력한다. 그런데 같은 계산기를 공유하는 사용자2가 5초 후 계산기의 메모리를 50으로 설정하고 10초 후 출력한다. 그러면 결국 둘 다 50이 출력된다.

 

이런 경우를 막으려면, 스레드가 사용중인 객체를 작업이 끝날 때까지 잠금을 걸어 다른 스레드가 사용할 수 없도록 해야 한다.

 

멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 리소스 영역을 임계 영역이라고 한다. critical section

자바는 임계 영역을 접근하기 위해 동기화 메소드를 제공한다. synchronized method

 

동기화 : 임계 영역에 여러 스레드가 접근하면 한 스레드가 동기화 메소드를 수행하는 동안 공유 자원을 lock하여 다른 스레드의 접근을 막는다.

 

스레드가 동기화 메소드를 실행하면, 즉시 (동기화 메소드가 속한) 해당 객체에 잠금을 걸어 다른 스레드가 동기화 메소드를 실행하지 못하도록 한다. 그리고 스레드가 동기화 메소드를 종료하면 객체 잠금이 풀린다.

 

동기화 메소드를 만들려면 메소드 선언에 synchronized 키워드를 붙인다. 인스턴스, 정적 둘 다 붙일 수 있다.

public synchronized void hi(){...}

 

만약 동기화 메소드가 여러 개가 존재하고, 그중 하나를 스레드가 실행하면 다른 스레드는 모든 동기화 메소드를 실행할 수 없다. 하지만 일반 메소드는 실행 가능하다.

 

다른 방법으로는 synchronized 블록을 사용할 수 있다. synchronized(객체참조){...} 블록을 선언하면 블록 내용이 실행되는 동안, "객체참조"에 lock이 걸린다.

'study > java' 카테고리의 다른 글

자바/JAVA LIFO, FIFO 컬렉션  (0) 2021.02.15
자바/JAVA 스레드(2)  (0) 2021.02.13
자바/JAVA 스트림과 입출력  (0) 2021.02.09
자바/JAVA Collection Framework  (0) 2021.02.05
자바/JAVA enum 열거  (0) 2021.01.29