inblog logo
|
jay0628
    Java

    [Java] 19. 스레드

    김주희's avatar
    김주희
    Feb 26, 2025
    [Java] 19. 스레드
    Contents
    1. 스레드2. 스레드 기본 사용법3. 스레드 객체의 상태 공유4. 5. 6. 스레드 제어7. 스레드의 데이터를 리턴 받아서 사용하는 경우
     

    1. 스레드

     

    2. 스레드 기본 사용법

     
    1. 스레드 객체 생성
    1. 스레드 시작 (start())
    1. 타겟 (run()) 만들기
    package ex19; // 스레드 기본 (스레드 객체 생성, 스레드 시작, 타켓(run) 만들기) public class Th01 { public static void sub1() { for (int i = 1; i <= 5; i++) { System.out.println("스레드 1: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void sub2() { for (int i = 1; i <= 5; i++) { System.out.println("스레드 2: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void main(String[] args) { // CPU -> 메인 스레드 // 람다식 한 줄이면 중괄호 생략 가능. void 타입이면 return 없어도 중괄호 생략 가능. // run 메서드 = () -> sub1() : Runnable 인터페이스를 통해 run 행위 전달 // 스레드의 제어권 - OS가 가짐 Thread t1 = new Thread(() -> sub1()); t1.start(); new Thread(() -> sub2()).start(); } }
     

    3. 스레드 객체의 상태 공유

      package ex19; public class Th02 { static String product = null; public static void main(String[] args) { Thread supp = new Thread(() -> { try { Thread.sleep(10000); product = "바나나깡"; } catch (InterruptedException e) { throw new RuntimeException(e); } }); supp.start(); Thread lis = new Thread(() -> { while (true) { try { Thread.sleep(500); if (product != null) { System.out.println("상품이 입고되었습니다. : " + product); break; } } catch (InterruptedException e) { throw new RuntimeException(e); } } }); lis.start(); } }
       

      4.

       
      package ex19; // 화가 // 스레드 사용시 대기가 없어지는 건 아님, 사용자 UX가 좋아짐 class MyFile { public void write() { try { Thread.sleep(5000); System.out.println("파일 쓰기 완료"); } catch (InterruptedException e) { throw new RuntimeException(e); } } } class 화가 { public void 그림그리기() { System.out.println("그림 그리기 완료"); } } public class Th03 { public static void main(String[] args) { MyFile myFile = new MyFile(); 화가 painter = new 화가(); // 비동기 프로그램 -> 좋은 UX 가능 painter.그림그리기(); new Thread(() -> myFile.write()).start(); painter.그림그리기(); } }
       

      5.

        package ex19; import java.util.*; // 1. 상속 받기 // 2. 상태 지정 // 3. run 재정의해서 사용 class MyTread extends Thread { List<Integer> list; @Override public void run() { // run 재정의해서 target 만들기 for (int i = 0; i < 10; i++) { System.out.println("MyTread: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } // ArrayList보다 추상적인 List에 의존 public MyTread(List<Integer> list) { this.list = list; } public List<Integer> getList() { return list; } // 컬렉션은 setter보다 add (한 건씩 추가하니까) public void addList(int num) { list.add(num); } } // 클래스로 스레드 만들기 (스레드별 상태 보관) public class Th04 { public static void main(String[] args) { // MyTread가 List를 has // MyTread가 ArrayList에 의존 MyTread t1 = new MyTread(new ArrayList<>()); t1.start(); } }
         
         

        6. 스레드 제어

         
        package ex19; import javax.swing.*; public class Th05 extends JFrame { private boolean state = true; private int count = 0; private int count2 = 0; private JLabel countLabel; private JLabel count2Label; public Th05() { setTitle("숫자 카운터 프로그램"); setVisible(true); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 레이아웃 매니저 설정 setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); // 숫자를 표시할 레이블 생성 countLabel = new JLabel("숫자1: " + count); count2Label = new JLabel("숫자2: " + count2); countLabel.setAlignmentX(CENTER_ALIGNMENT); count2Label.setAlignmentX(CENTER_ALIGNMENT); add(countLabel); add(count2Label); // 멈춤 버튼 생성 JButton increaseButton = new JButton("멈춤"); increaseButton.setAlignmentX(CENTER_ALIGNMENT); add(increaseButton); // 버튼에 액션 리스너 추가 // while 돌면서 특정한 시간을 두고 멈춤 버튼만 지켜보고 있음 // 감시하고 있는데 버튼 클릭하면 내가 뭘 할지 모르겠어 -> 행위({}) 전달 // 추상 메서드 increaseButton.addActionListener(e -> { state = false; }); new Thread(() -> { while (state) { try { Thread.sleep(1000); count++; countLabel.setText("숫자1 : " + count); } catch (InterruptedException ex) { throw new RuntimeException(ex); } } }).start(); new Thread(() -> { while (state) { try { Thread.sleep(1000); count2++; count2Label.setText("숫자2 : " + count2); } catch (InterruptedException ex) { throw new RuntimeException(ex); } } }).start(); } // 스레드 제어 예제 public static void main(String[] args) { new Th05(); } }
         

        7. 스레드의 데이터를 리턴 받아서 사용하는 경우

        1. 타이밍

        1. ㅇ
            package ex19; //콜백 class Store implements Runnable { int qty; @Override public void run() { // 통신 -> 다운로드 try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } qty = 5; } } /* 스레드에서 받은 데이터를 리턴 받아서 응용하고 싶을때!! 1. 타이밍 맞추기 (임시방편 - 그래도 쓰는 사람 많음) - 통신에서는 불가 2. 리스너 (부하가 너무 큼) 3. 콜백 (제일 좋음) */ public class Th06 { public static void main(String[] args) { int totalQty = 10; Store store = new Store(); Thread t1 = new Thread(store); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("재고수량 :" + (store.qty + totalQty)); } } // 스레드를 쓰는 이유 // 1. 동시에 쓰고 싶을 때 // 2. I/O가 일어날 때 - hdd 접근해서 꺼내서 가져오고~ 오래 걸린다 // I/O = 오래 걸리는 일과 유사 // 느림 -> context switching, 스레드 객체가 계속 만들어짐
         
         

        2. 리스너

        1. ㅇ
        1. 다른일도 하면 스레드 필요하다. 예제 코드에서는 필요하지 x니까~~
        package ex19; //콜백 class Store implements Runnable { Integer qty; @Override public void run() { // 통신 -> 다운로드 try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } qty = 5; } } /* 스레드에서 받은 데이터를 리턴 받아서 응용하고 싶을때!! 1. 타이밍 맞추기 (임시방편 - 그래도 쓰는 사람 많음) - 통신에서는 불가 / CPU가 그냥 쉰다 2. 리스너 (부하가 너무 큼) - CPU가 쉬지 않고 지켜봄 3. 콜백 (제일 좋음) */ public class Th06 { public static void main(String[] args) { int totalQty = 10; Store store = new Store(); Thread t1 = new Thread(store); t1.start(); while (true) { // store.qty가 int이면 null이 안들어감 -> Integer Wrapper 클래스로 쓰는 것이 낫다 if (store.qty != null) break; // 너무 빠르게 돌리면 안되니까 sleep // 금방 끝나지만 너무 많은 연산이 돌아감 try { Thread.sleep(10); System.out.println("지켜본다"); } catch (InterruptedException e) { throw new RuntimeException(e); } } System.out.println("재고수량 :" + (store.qty + totalQty)); } }
         

        3. 콜백 (중요

        1. ㄹㅇ
          1. package ex19; // 콜백 // (1) 콜백 메서드 만들기 // 인터페이스와 함수 하나 만들어서 리턴 받고 싶은 파라미터를 만들어주면 된다. interface Callback { void 입고(int gty); } class Store implements Runnable { Integer qty; Callback callback; // (2) 리턴이 필요한 곳으로 가서 콜백 메서드 전달받기 public Store(Callback callback) { // 행위 전달 받은 store this.callback = callback; } @Override public void run() { // 통신 -> 다운로드 try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } qty = 5; // 끝나면 입고 메서드 호출해주니까 콜백(? callback.입고(qty); // (3) 종료 시 콜백 메서드 호출 } } /* 스레드에서 받은 데이터를 리턴 받아서 응용하고 싶을때!! 1. 타이밍 맞추기 (임시방편 - 그래도 쓰는 사람 많음) - 통신에서는 불가 / CPU가 그냥 쉰다 2. 리스너 (부하가 너무 큼) - CPU가 쉬지 않고 지켜봄 3. 콜백 (제일 좋음) */ public class Th06 { public static void main(String[] args) { int totalQty = 10; Store store = new Store(qty -> { System.out.println("재고수량 :" + (qty + totalQty)); }); Thread t1 = new Thread(store); t1.start(); } } // 스레드를 쓰는 이유 // 1. 동시에 쓰고 싶을 때 // 2. I/O가 일어날 때 - hdd 접근해서 꺼내서 가져오고~ 오래 걸린다 // I/O = 오래 걸리는 일과 유사 // 느림 -> context switching, 스레드 객체가 계속 만들어짐
         
         
         
         
         
         
         
         
         
         
         
        package ex19; public class Th01 { public static void main(String[] args) { // 모든 스레드는 상태 변수를 공유 -> 동시에 접근하면 교착 상태 //static int num = 5000; // main thread의 target = main method // CPU는 Java 실행될 때 main 스레드를 준다. // 분신 만들기 = 이것만의 새로운 생명 주기를 만드는 것 // main thread가 분신 하나 만들고 start // Runnable의 run 구현 -> 람다식으로 Thread t1 = new Thread(() -> { //t1의 생명 주기 = target // 분신1 for (int i = 0; i < 10; i++) { System.out.println("분신 1: " + i); //System.out.println(num); try { Thread.sleep(500); // 0.5초 } catch (InterruptedException e) { throw new RuntimeException(e); } } }); // 이 스레드만의 메인 메서드가 필요 = target // 메인 스레드가 start를 호출하면 start: os야 남는 스레드 있으면 만들어서 run 좀 호출해줘 (run = call back method t1.start(); // main thread가 분신 하나 만들고 start Thread t2 = new Thread(() -> { // 분신2 for (int i = 0; i < 10; i++) { System.out.println("분신 2: " + i); try { Thread.sleep(500); // 0.5초 } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t2.start(); Thread t3 = new Thread(() -> { // 분신 3 for (int i = 0; i < 10; i++) { System.out.println("분신 3: " + i); try { Thread.sleep(500); // 0.5초 } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t3.start(); System.out.println("메인 스레드 종료"); // 실행 -> context switching으로 t1 target 내용 실행되고 ~ => 메인 스레드가 먼저 종료됨 but java는 종료되지 않음 // 분신이 종료되어야 메인 thread도 종료됨 } } //인터페이스 메서드 전달 역할도 함 -> 람다로
        Share article

        jay0628

        RSS·Powered by Inblog