[JAVA] Thread - 1

Coastby·2022년 11월 6일
0

JAVA

목록 보기
28/33

1. 프로세스와 쓰레드

  • 프로세스 : 실행 중인 프로그램, 자원 (resource)과 쓰레드로 구성
  • 쓰레드 : 프로세스 내에서 실제 작업을 수행
    • 모든 프로세스는 최소한 하나의 쓰레드를 가지고 있다.
    • 프로세스 : 쓰레드 = 공장 : 일꾼
    • 싱글 쓰레드 프로세스 = 자원 + 쓰레드
    • 멀티 쓰레드 프로세스 = 자원 + 쓰레드 + 쓰레드 + … + 쓰레드 → 대부분의 프로그램

2. 멀티쓰레드의 장단점

💡 하나의 새로운 프로세스를 생성하는 것보다 하나의 새로운 쓰레드를 생성하는 것이 더 적은 비용이 든다.

  • 자바가 멀티쓰레드를 지원하며 1990년대말 CGI (단일쓰레드만 제공)를 사용하던 웹이 자바로 많이 넘어왔다.

  • CPU의 코어가 한 번에 단 한나의 작업만 수행할 수 있으므로, 실제로 동시에 처리되는 작업의 개수는 코어의 개수와 일치한다.

  • 그러나 처리해야하는 쓰레드의 수는 언제나 코어의 개수보다 훨씬 많기 때문에 각 코어가 아주 짧은 시간 동안 여러 작업을 번갈아 가며 수행함으로써 여러 작업들이 모두 동시에 수행되는 것처럼 보이게 한다.

  • 그래서 프로세스의 성능이 단순히 쓰레드의 개수에 비례하는 것은 아니며, 하나의 쓰레드를 가진 프로세스보다 두 개의 쓰레드를 가진 프로세스가 오히려 더 낮은 성능을 보일 수도 있다.

  • 여러사용자에게 서비스를 해주는 서버 프로그램의 경우 멀티쓰레드로 작성하는 것은 필수적이어서 하나의 서버 프로세스가 여러 개의 쓰레드를 생성해서 쓰레드와 사용자의 요청이 일대일로 처리되도록 프로그래밍해야 한다.

    • 만일 싱글쓰레드로 프로그램을 작성한다면 사용자의 요청마다 새로운 프로세스를 생성해야하는데 프로세스를 생성하는 것이 더 많은 비용이 든다.
    • 쓰레드를 가벼운 프로세스, 즉 경량 프로세스 (LWP, light-weight process)라고 부르기도 한다.

장점

  • 시스템 자원을 보다 효율적으로 사용할 수 있다.
  • 사용자에 대한 응답성 (responseness)이 향상된다.
  • 작업이 분리되어 코드가 간결해진다.

단점

  • 동기화 (synchronization)에 주의해야 한다.

  • 교착상태 (dead-lock)가 발생하지 않도록 주의해야 한다.

    • 교착상태 : 두 쓰레드가 자원을 점유한 상태에서 서로 상대편이 점유한 자원을 사용하려고 기다리느라 진행이 멈춰있는 상태
  • 각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야한다.

    • 기아 : 특정 쓰레드가 작업할 기회를 갖지 못하고 작업이 진행되지 않는다.

    → 프로그래밍할 때 고려해야 할 사항들이 많다.

“멀티쓰레드를 짜는 것이 어려운 것이 아니라 효율적으로 돌아가게 짜는 것이 어렵다.”

3. 쓰레드의 구현과 실행

○ 쓰레드의 구현

자바에서는 쉽게 멀티쓰레드를 구현할 수 있도록 미리 작성된 클래스들을 제공하고 있기 때문에 기본 개념만 알아도 도움이 된다.

구현하는 방법은 두 가지가 있고, 어느 쪽이든 큰 차이는 없다. 하지만 상속을 받게되면 다른 클래스를 상속받을 수 없기 때문에 인터페이스를 구현하는 방법이 일반적이다.

1) Thread 클래스를 상속 : 단일 상속이라 다른 클래스를 상속할 수 없다

class MyThread extends Thread {
		public void run(){ 
				/* 쓰레드가 수행할 작업 내용 */
		}
}
MyThread t = new MyThread();
t.start()

2) ✅ Runnable 인터페이스를 구현 → better!

  • 재사용성 (reusability)가 높고, 코드의 일관성 (consistency)을 유지할 수 있기 때문에 보다 객체지향적인 방법이다.
class MyThread implements Runnable {
		public void run(){  //Runnable 인터페이스의 추상메서드 run()을 구현한다.
				/* 쓰레드가 수행할 작업 내용 */
		}
}
Runnable r = new MyThread();
Thread t = new Thread(r);
//Thread t = new Thread(new MyThread());
t.start();

○ 쓰레드의 실행

  • 쓰레드를 생성한 후에 start()를 호출한다.
  • start() :
    • 실행가능한 상태가 되는 거지, 바로 실행하는 것은 아니다. 실행순서는 OS의 스케줄러가 결정한다.
    • 먼저 실행했다고 먼저 실행되는 것도 아니다.
    • JVM이 OS에 독립적이지만, 종속되는 몇 가지 중의 하나가 쓰레드이다.
    • 한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다. (하나의 쓰레드에 start()를 두 번 이상 호출하면 IllegalThreadStateException 발생)
  • run()을 작성하고 start()를 호출하는 이유

1. main 메서드에서 쓰레드의 start()를 호출한다.
2. start()는 새로운 쓰레드를 생성하고, 쓰레드가 작업하는데 사용될 호출스택을 생성한다.
3. 새로 생성된 호출스택에 run()이 호출되어, 쓰레드가 독립된 공간에서 작업을 수행한다.
4. 이제는 호출스택이 2개이므로 스케줄러가 정한 순서에 의해서 번갈아 가면서 실행된다.
- run()을 호출하면 그냥 main에서 run()이 실행되는 싱글쓰레드가 된다.

○ main 쓰레드

main 메서드의 코드를 수행하는 쓰레드

  • 사용자 쓰레드 : 메인 스레드
  • 데몬 쓰레드 : 보조 쓰레드

실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.

메인 쓰레드가 끝나도 실행 중인 쓰레드가 있다면 프로그램은 종료되지 않는다.

4. 싱글쓰레드와 멀티쓰레드

두 개의 쓰레드로 작업한 시간이 싱글쓰레드로 작업한 시간보다 더 걸리게 되는데 그 이유는 쓰레드간의 작업 전환 (context switching)에 시간이 걸리기 때문이다.

예제에서는 화면 (console)이라는 자원을 놓고 두 쓰레드가 경쟁하게 된다. 한 쓰레드가 화면에 출력하고 있는 동안 다른 쓰레드는 출력이 끝나기를 기다려야하기 때문이다.

하지만, 멀티쓰레드를 사용하면 아래와 같은 장점이 있다

1) 여러 작업을 동시에 처리할 수 있다

2) 자원을 효율적으로 쓸 수 있다.

  • 두 쓰레드가 서로 다른 자원을 사용하는 작업을 하는 경우 멀티 쓰레드가 효율적이다.
  • 예를 들어, 사용자로부터 데이터를 입력받는 작업, 네트워크로 파일을 주고받는 작업, 출력하는 작업 등 입출력을 필요로 하는 경우가 이에 해당한다. (I/O 블락킹에서 자유로움)
  • I/O Blocking : 입출력 시 작업이 중단되는 것
  • 만약 입력 받는 작업과 출력하는 작업이 싱글 쓰레드로 처리하면 입력을 기다리는 동안 아무 일도 못하고 기다려야 할 것이다.
  • 예제
//싱글스레드 : 사용자 입력을 기다리는 구간이 생긴다(I/O blocking)
class ThreadEx6{
	public static void main(String[] args){
    	String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
        System.out.println("입력하신 값은 " + input + "입니다.");
        
        for (int i = 10; i > 0; i--){
        	System.out.println(i);
            try {Thread.sleep(1000);} catch (Exception e) {}
        }
    }
}

//멀티스레드 : 사용자가 입력을 마치지 않아도 화면에 숫자가 출력된다.
class ThreadEx7{
	public static void main(String[] args){
    	ThreadEx7_1 th1 = new ThreadEx7_1();
        th1.start();
        
    	String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
        System.out.println("입력하신 값은 " + input + "입니다.");
    }
}

class ThreadEx7_1 extends Thread{
	public void run(){
    	for (int i = 10; i > 0; i--){
        	System.out.println(i);
            try {sleep(1000);} catch (Exception e) {}
        }
    }
}
profile
훈이야 화이팅

0개의 댓글