Cohe

16. ParallelStream, Thread 본문

개발 언어/JAVA

16. ParallelStream, Thread

코헤0121 2024. 3. 27. 16:44
728x90
  • alt + shift + o : 자동 import

ParallelStream

  • 사용시 병렬 스트림을 쉽게 사용할 수 있다.
  • 전체요소를 서브요소로 나누거, 각 서브요소들에게 개별 스레드를 생성해준다.
  • 결과를 결합하여 최종 처리 결과를 리턴
  • Stream<Object> stream = list.prarllelstream();
    • 일반 스트림은 이렇게 쓴다 : Stream<Object> stream = list.stream();
  • isParallel() 메서드로 병렬 여부를 확인 가능.
  • 스트림 연결하기 :Steam.concat()
    • 두 개의 스트림 객체를 연결해서 하나의 새로운 스트림 객체를 생성

스레드

  • 프로세스란 실행중인 프로그램을 의미
  • 스레드란 프로세스 안에서 작업 수행하는 것
  • 프로세스에는 반드시 하나 이상의 스레드가 존재합니다.
package H_java;

public class H01_processEx {
    //스레드 (동시에 여러 프로그램 실행)
    public static void main(String[] args) {
        //프로세스 실행
        try {
            //메모장 실행
            Process p1 = Runtime.getRuntime().exec("notepad.exe");

            //그림판 실행
            Process p2 = Runtime.getRuntime().exec("mspaint.exe");

            p1.waitFor(); //p1 프로세스가 종료될 때까지 대기
            p2.destroy(); //p1 프로세스가 종료되면 실행함. destroy 강제 종료 시킨다.
           

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

}
  • 멀티 스레드의 장점
    1. cpu, 메모리를 효율적으로 사용할 수 있다.
    2. 사용자 응답성이 높아진다.
    3. 작업을 스레드 단위로 분리해서 실행 시간을 줄여 줄 수 있다.
  • 멀티 스레드의 단점
    1. 동기화, 공유 자원의 문제(교착상태 - dead lock) → 식사하는 철학자 문제인 것 같음
  • 스레드 생성하는 방법
    1. Thread 클래스를 상속하는 방법
    2. Runnable 인터페이스를 구현한 객체를 Thread 객체에 넣어주는 방식
package H_java;

public class H02_threadEx {
    public static void main(String[] args) {
        Thread t1= new ThreadExtends();
        t1.run();

        Runnable r = new RunnableImpl();
        Thread t2 = new Thread(r);
        //동시에 개별적으로 실행을 한다!!!


        //익명 함수를 이용해서 구현
        Thread t3 = new Thread(new Runnable() {
           
            @Override
            public void run() {
                System.out.println("Runnable 구현하는 방법으로 실행");
                for(int i=0;i<50;i++){
                    System.out.println("Thred3 : "+i);
                }    
            }
        });

       
        // 람다식 표현
        Thread t4 = new Thread(() -> {
            System.out.println("t4 스레드 시작");
            for (int i = 0; i < 500; i++) {
                System.out.println("Thread4: "+i);
            }
        });


        // 스레드 우선순위
        //  스레드가 가질 수 있는 우선 순위는 1 ~ 10 사이의 숫자로 10이 가장 높은 우선순위임.
        //  중요! 스레드의 우선순위는 절대적 수치가 아닌 상대적 수치
        //  우선 순위에 따라서 점유시간과 자원이 주어지게 된다.
       
        //  우선순위 적용방법 - setProperty(int)   1 ~ 10
         
        //  우선순위 적용시 주의 사항
        //   1. 우선순위는 상대적 값이다.
        //   2. 스레드를 실행하기 전에 지정해야 한다.
        //   3. main() 메서드에서 실행하는 스레드의 경우 기본값 5
        //  직접 스레드의 우선 순위값을 설정을 하는 것보다 Thread의 static final 필드
        //  값을 이용하는 것이 더 좋다.

        // 우선 순위 지정( 1 ~ 10, 상수값 이용)
        t1.setPriority(Thread.NORM_PRIORITY);
        t2.setPriority(Thread.NORM_PRIORITY);
        t3.setPriority(Thread.NORM_PRIORITY);
        t4.setPriority(Thread.MAX_PRIORITY);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        System.out.println("main thread 종료");

    }
}


//Thread를 상속하는 방식
class ThreadExtends extends Thread{
    @Override
    public void run() {
        System.out.println("스레드 상속 받는 방법으로 실행");
        for(int i=0;i<50;i++){
            System.out.println("Thread Extends : " + i);
        }
       
    }
}

//Runnable 구현체
class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable 구현하는 방법으로 실행");
        for(int i=0;i<50;i++){
            System.out.println("RunnableImpl : "+i);
        }
       
    }

   
}
  • Thread 클래스 상속 - run() 메서드
  • Runnable 구현 - run() 추상 메서드
  • 스레드가 가질 수 있는 우선 순위는 1~10 사이의 숫자로 10이 가장 높은 우선순위임
  • 중요! 스레스의 우선순위는 절대적 수치가 아닌 상대적 수치
  • 우선 순위에 따라서 점유 시간과 자원이 주어지게 된다
  • 우선순위 적용방법 - setProperty(int) 1~10
  • 우선순위 적용시 주의 사항
    1. 우선순위는 상대적 값이다.
    2. 스레드를 실행하기 전에 지정해야 한다.
    3. main()메서드에서 실행하는 스레드의 경우 기본 값은 5다
      • 직접 스레드의 우선 순위값을 설정을 하는 것보다 Thread의 static final 필드 값을 이용하는 것이 더 좋다.
package H_java;

public class H03_ThreadEx {
    public static void main(String[] args) {
        //sleep()
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                int i = 1;
                while(true) {
                    //interrupt 확인
                    System.out.println("t1 : "+i);
                    i++;
                    if (Thread.interrupted()) {   //interrupt 발생 여부 확인
                        System.out.println("인터럽트가 발생했었네요. 반복문 종료합니다.");
                        break;
                    }

                    // sleep 확인
//                  try {
//                      Thread.sleep(1000);  //1초
//                  } catch (InterruptedException e) {
//                      System.out.println(e.getMessage());
////                        System.exit(0);
//                  }
                }
                System.out.println("스레드 실행 종료");

            }
        });

        t1.start();


        //------------------------------------
        // interrupt() 메서드
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
        t1.interrupt();  // 스레드가 일시 정지 상태이면 예외 발생. sleep interrupted

        // join() 메서드
        Sum t2 = new Sum();
        Sum t3 = new Sum();

        t2.start();
        t3.start();
        try {
            t2.join();   // t2 스레드가 종료될때까지 대기
            t3.join();   // t3 스레드가 종료될때까지 대기
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
        System.out.println("두 스레드의 Sum의 합계 : "+(t2.sum + t3.sum));

        //yeild() 일드
        YeildThread t4 = new YeildThread();
        YeildThread t5 = new YeildThread();
        t4.start();
        t5.start();

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
        }
       
        t4.iscontinue = false; //t4 스레드 yeild() 즉, 양보

        try {
            Thread.sleep(100);
        } catch (Exception e) {        }

        t4.iscontinue = true;
        try {
            Thread.sleep(20);
        } catch (Exception e) {}

        t4.isbreak = true;
        t5.isbreak = true;

    }
}

class Sum extends Thread {
    int sum = 0;
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
    }
}

class YeildThread extends Thread{
    boolean isbreak = false;
    boolean iscontinue = true;

    @Override
    public void run() {
        while(!isbreak){
            if(iscontinue){
                System.out.println(getName()+" 실행중 >.<");
            }
            else{
                System.out.println("양보합니다.");
                Thread.yield(); //실행중이 아니라면 양보해라
            }
        }
        System.out.println(getName()+" 종료");
    }
}

  • 스레드 상태 제어
  • 스레드 작업 시 프로그램 중지 및 버그가 발생할 수 있는 부분
    • sleep() : 원하는 시간만큼 멈추고 싶을 때 사용, InterruptedException 처리를 강제하기 때문에 try구분 사용 (checked입니다.)
    • interrupt : 스레드가 일시정지 상태에 있을 때 예외를 발생시킨다.nterruptedException 예외 발생 시 run()메서드가 정상 종료 시킬 수 있게 한다
    • join () : 다른 스레드 실행이 완료 될 떄 까지 기다리는 메서드
    • yield() : 다른 스레드에게 실행 양보하는 메서드

 

  • 스레드 동기화
    • 여러 스레드가 동작하는 프로그램에서 하나의 객체를 함께 사용하는 경우 의도치 않은 결과를 반환할 수 있다.
    • 하나의 스레드가 작업이 끝날 때까지 객체가 변경되지 않도록 하는 것을 동기화 (synchronized)
    • 동기화 지정 방법 → synchronized 키워드 사용
      1. 동기화 메서드 이용
package H_java;

public class H04_ThreadEx {

    public static void main(String[] args) {
        SmartPhoneGame game = new SmartPhoneGame();
        Player1 p1 = new Player1();
        p1.setGame(game);
        p1.start();

        Player2 p2 = new Player2();
        p2.setGame(game);
        p2.start();
       
    }
}
class SmartPhoneGame{
    private int level;

    public int getLevel() {
        return level;
    }
    public synchronized void increaseLevel(){
        while (true) {
            this.level ++;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                }
               
                //현재 스레드의 이름과 레벨 출력
                System.out.println(Thread.currentThread().getName()+" LEVEL : "+this.level);
                if(this.level%10==0){
                    break;
            }
        }
    }
}

class Player1 extends Thread{
    private SmartPhoneGame game;
    public void setGame(SmartPhoneGame game) {
        this.game = game;
        this.setName("Player1");
    }
    public SmartPhoneGame getGame() {
        return game;
    }
    @Override
    public void run() {
        game.increaseLevel();
    }
}
class Player2 extends Thread{
    private SmartPhoneGame game;
    public void setGame(SmartPhoneGame game) {
        this.game = game;
        this.setName("Player2");
    }
    public SmartPhoneGame getGame() {
        return game;
    }
    @Override
    public void run() {
        game.increaseLevel();
    }
}
  •  동기화 블럭 :
class SmartPhoneGame{
    private int level;

    public int getLevel() {
        return level;
    }
    public synchronized void increaseLevel(){
        synchronized(this){//동기화 블럭
            while (true) {
                this.level ++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {}
                   
                //현재 스레드의 이름과 레벨 출력
                System.out.println(Thread.currentThread().getName()+" LEVEL : "+this.level);
                    if(this.level%10==0){
                        break;
                }
            }
        }
       
    }
}

 

  • 동기화에서 사용하는 스레드 작업 메소드
    • wait(), notify() : 같이 쓰인다! 객체 자체를 잠그고 작업에 대한 결정권은 notify가 갖는다. 다른 스레드가 실행된다.
    • notifyAll() : 멈췄던 애들이 다 실행
package H_java;

public class H06_ThreadEx {
    public static void main(String[] args) {
        //스레드 객체 생성
        DaemonThread dt1 = new DaemonThread();
        DaemonThread dt2 = new DaemonThread();

        //데몬 스레드로 설정
        dt1.setDaemon(true); //이걸로 데몬 스레드임을 알려준다!!
        dt2.setDaemon(true);

        dt1.start();
        dt2.start();

        try {
            Thread.sleep(5000);
        } catch (Exception e) { }
        System.out.println("main 종료");
        //종료하면 멈춰버린다.
    }
}

class DaemonThread extends Thread{

    @Override
    public void run() {
        while (true) {
            System.out.println(getName());
            try {
                sleep(500);
            } catch (InterruptedException e) {  }
        }
    }
}

 

  • ThreadGroup
    • 스레드 그룹은 관련되어 있는 스레드 별로 그룹핑해서 다루기 위해 사용
    • 그룹 안에 또 다른 그룹을 생성하는 것도 가능
    • 보안상의 이유로 자신과 하위 그룹을 변경이 가능하다. 하지만 다른 그룹에 대한 모든 변경은 불가능하다.
    • 관련 메소드
      • activeCount() : 현재 그룹 및 하위 그룹의 모든 스레드 수 리턴
      • activeGroupCount() : 현재 그룹의 모든 하위 그룹 수 리턴
      • getMaxPriority() : 현재 그룹에 스레드가 가질 수 있는 최대 우선 순위 값 리턴
      • setMaxPriority(int priority) : 최대 우선순위 값 설정
      • getName() : 현재 그룹의 이름 리턴
      • getParent() : 현재그룹의 부모 그룹 리턴
      • parentof(ThreadGroup g) : 현재 그룹이 g의 부모그룹인지 여부 리턴
      • isDaemon() : 현재 그룹이 Daemon 그룹인지 여부 리턴
      • setDaemon(boolean daemon) : 데몬 그룹으로 설정
      • list() : 현재 그룹에 포함된 스레드와 하위 그룹 정보 출력
      • intrrupt : 현재 그룹에 포함된 모든 스레드 중지
      • 동작 이제 안함!!
        • checkAccess() : 스레드 그룹을 변경할 권한이 있는지 여부를 리턴한다.
        • destroy() : 현재 그룹의 모든 하위 그룹을 삭제한다.
        • isDestroyed() : 현재 그룹이 삭제 되었는지 여부 확인
package H_java;

public class H07_ThreadEx {
    public static void main(String[] args) {
        //스레드 설정 그룹
        //1. 그룹 객체 생성
        ThreadGroup group = new ThreadGroup("group1");
       
        //2. 스레드 그룹에 스레드 추가
        MyThread t1 = new MyThread(group, "t1");
        MyThread t2 = new MyThread(group, "t2");
        MyThread t3 = new MyThread(group, "t3");

        //3. 스레드 그룹 시작
        t1.start();
        t2.start();
        t3.start();

        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            // TODO: handle exception
        }
       
        group.interrupt();
        System.out.println("그룹 스레드 숫자 : "+group.activeCount());
        System.out.println("그룹 숫자 : "+group.activeGroupCount());

        group.checkAccess();

        group.destroy();
        group.isDaemon();

        System.out.println("그룹의 부모를 리턴 "+ group.getParent());

        group.list();
    }

}

class MyThread extends Thread{

    public MyThread(ThreadGroup g, String name){
        super(g,name);

    }
    @Override
    public void run() {
        while(true){
            System.out.println(getName());
            try {
                sleep(500);
            } catch (Exception e) {
                System.out.println(e.getMessage() + "interrupt 발생");
                break;
            }
        }
        System.out.println(getName()+ "종료");
    }
}

 

 

 

'개발 언어 > JAVA' 카테고리의 다른 글

고객 관리 프로그램 작성  (0) 2024.03.31
17. Network, 서버 만들기!  (0) 2024.03.28
15. Operator, Stream  (0) 2024.03.27
14 람다식  (0) 2024.03.25
13 객체 정렬을 위한 객체 간 크기 비교, 제너릭 , File 입출력  (0) 2024.03.21