[JAVA] 멀티 스레딩(multi-threading)

Jan 03, 2024
[JAVA] 멀티 스레딩(multi-threading)
 

프로세스와 스레드의 차이

컴퓨터에는 프로세스와 스레드라는 2가지의 실행 단위가 있다. 가장 근본적인 차이는 프로세스는 자신만의 데이터를 갖는데 반해, 스레드는 모두 동일한 데이터를 공유한다. 동시에 수행되는 스레드들이 데이터를 공유한다는 것이 위험할 수 있지만 스레드 간의 통신은 효율적이다. (멀티 스레딩) 위험한 이유) 1. 경쟁 상태(실행 순서에 따른 문제), 2. 데이터 일관성 문제, 3. 데드락(한무 대기)

멀티 스레딩(multi-threading)

장점

  • 하나의 스레드로 여러 코어를 최대로 활용하여 서버의 응답 시간을 최소화 할 수 있다.
    • 멀티 스레딩이 CPU를 더 빠르게 굴리진 않는다. 하지만 RAM과 Hard disk가 I/O하는 동안에 컨텍스트 스위칭(Context Switching)을 하여 일을 효율적으로(쉬지않고) 처리합니다.

단점

  • 멀티 스레딩 프로그램은 단일 스레드 프로그램보다 신경 써야 할 부분이 많은데, 동시에 여러 스레드들이 같은 데이터를 공유하게 되면 동기화라고 하는 까다로운 문제가 발생하기 때문이다. ex) 3자리에 남은 예약석을 2명이 서버에 동시에 2자리씩 요청하는 경우…
 

스래드 생성과 실행

자바에서 스레드를 생성하여 작업을 실행하는 방법은 다음과 같다. (2개)
  • Thread 클래스를 상속하기
  • Runnable 인터페이스를 구현하기
 

Thread 클래스

Thread 클래스를 상속받아 자식 클래스를 만들고 run() 메소드를 재정의하는 방법. 자식 클래스의 인스턴스를 생성, start() 메소드를 호출하면 스레드가 실행된다.
 
class MyThread extends Thread{ // 1. Thread를 상속받기 public void run(){ // 2. run() 메소드를 재정의 for (int i = 0; i <= 10; i++) { System.out.print(i + " "); } } } public class MyThreadTest1 { public static void main(String[] args) { Thread t = new MyThread(); // 3. Thread 객체 생성 t.start(); // 4. start() 메소드를 호출 -> 스레드 시작 } }
notion image
 
 
0부터 10까지 화면에 출력
 
스레드가 실행하는 모든 작업은 이 run() 메소드 안에 있어야 한다.
 

Runnable 인터페이스

Thread 클래스를 확장하는 방법에 큰 단점이 있는데요, 자바에선 단일 상속만 가능하여, 다른 클래스에서 상속받은 클래스는 스레드로 만들 수 없다는 점입니다.
이런 경우에 Runnable 인터페이스를 구현하는 방법을 사용해야 합니다. Runnable 인터페이스 안은 run() 메소드만 정의되어 있습니다. 구현한 객체를 Thread 클래스의 생성자로 넘기면 됩니다. 네. 결국 Thread 클래스를 사용해야 합니다.
 
class MyRunnable implements Runnable { // 1. Runnable 인터페이스를 구현하기 public void run(){ // 2. run() 메소드 작성 for (int i = 0; i <= 10; i++) { System.out.print(i + " "); } } } public class MyThreadTest2 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); // 3. Thread 객체 생성, MyRunnable 객체를 인수로 전달 t.start(); // 4. start() 호출 } }
notion image

⚠️ run() 메소드를 직접 호출 하면 안 된다. start() 메소드 사용하기

 

예제

스레드 2개 만들어보기
class MyRunnable3 implements Runnable{ String myName; public MyRunnable3(String name){ myName = name; } public void run(){ for (int i = 0; i < 10; i++) { System.out.print(myName + i + " "); } } } public class TestThread { public static void main(String[] args) { Thread t1 = new Thread(new MyRunnable3("A")); Thread t2 = new Thread(new MyRunnable3("B")); t1.start(); t2.start(); } }
notion image
notion image
2개의 스레드가 실행되면서 스레드의 출력이 섞이는 것을 볼 수 있습니다.
 
 

람다식을 이용한 스레드 작성

람다식을 이용하면 얼마나 프로그램이 간단하게 작성되는지 살펴보는 시간~
public class LambdaTest { public static void main(String[] args) { Runnable task = () -> { for (int i = 0; i <= 10; i++) System.out.print(i + " "); }; new Thread(task).start(); } }
notion image
 
0부터 10까지 1초 단위로 카운트하는 애플리케이션을 그래픽 모드로 작성하기
import javax.swing.*; import java.awt.*; public class CountDownTest extends JFrame { private JLabel label; class MyThread extends Thread{ // 스레드를 내부 클래스로 만들면 필드에 접근이 쉽다 public void run(){ for (int i = 0; i <= 10; i++) { try { Thread.sleep(1000); // 1초 동안 스레드를 일시 정지 } catch (InterruptedException e) { // sleep()메서드는 도중에 예외가 발생할 e.printStackTrace(); // 가능성이 있어 try-catch블록처리 } label.setText(i + ""); // 1초가 지나면 label의 텍스트를 변경 } } } public CountDownTest(){ setTitle("카운트 다운"); setSize(400,150); label = new JLabel("Start"); label.setFont(new Font("Serif", Font.BOLD, 100)); add(label); setVisible(true); (new MyThread()).start(); // 스레드 시작 } public static void main(String[] args) { CountDownTest t = new CountDownTest(); } }
InterruptedException는 스레드가 일시정지 상태에서 interrupt()메서드에 의해 깨어나는 예외입니다.
notion image
notion image
notion image
Start - 1 - 2 - … - 10 까지 출력된다
Share article

MiracleCoding