본문 바로가기

신세게 - Java 공부

6주차 배운점 느낀점 - 운영체제, Thread, 스레드 상태, Synchronized

728x90
반응형

6주차

 

스레드에 대해 배웠다. 개념과 스레드의 상태, 동기화에 대해 배웠다.
내가 공부한 OS의 개념이 많이 나와서 다시 상기시키는 계기가 되었다.
수업듣는데 수월했지만 스레드를 배워보니 기능이많고 신경쓸게 많다.

 

배운점

 

멀티 프로세스와 멀티 스레드

 

운영체제
자원을 효율적이게 운영 관리하는 소프트웨어
운영과 관리를 위한 소프트웨어 두가지 목적을 가지고 만든 소프트웨어
소프트웨어와 하드웨어사이에 원활한 운영 소프트웨어 하드웨어 사용자가 편리하게 사용할 수있게 운영, 리소스 관리

 

프로세스
운영체제가 관리하는 실행 중인 프로그램 = 자원을 할당받았다

 

테스크(Task)
작업

 

멀티 태스킹
두 가지 이상의 작업을 동시에 처리하는 것, 운영체제가 멀티 프로세스를 생성해서 처리, 반드시 멀티태스킹이 멀티프로세스를 의미하지 않는다.
하나의 프로세스에서 멀티태스킹할 수 있도록 만든 대표적인 프로그램: 채팅
메신저 프로그램은 채팅 작업을 하면서 동시에 파일 전송이나, 선물하기, 연락처 전송하기, 초대하기
하나의 프로세스가 두 가지 이상의 작업을 처리할 수 있는 것은 멀티스레드 때문이다.
스레드란 코드의 실행 흐름인데 프로세스 내에 스레드가 두 개라면 두개의 코드 실행 흐름이 생기는 것이다.
멀티프로세스는 프로그램 단위의 멀티태스킹
멀티스레드는 프로그램 내부에서의 멀티 태스킹

 

스레드
코드의 실행 흐름, 독립적으로 끝까지 실행, 작은 실행 단위, 프로그램내부에 존재

 

멀티 스레드
두 개의 코드 실행 흐름. 두 가지 이상의 작업을 처리
멀티 프로세스 = 프로그램 단위의 멀티 태스킹
멀티 스레드 = 프로그램 내부에서의 멀티 태스킹

 

예외를 얼마나 꼼꼼하게 예측해서
데이터 분할해서
데이터를 어떻게 보관해서 잘 처리하는지 , 데이터 무결성 제공 (결점 없이, 신뢰할 수 있는 데이터 유지보관)

 

메인 스레드

 

메인 스레드는 main() 메소드의 첫 코드부터 순차적으로 실행
main() 메소드의 마지막 코드를 실행하거나 return 문을 만나면 실행을 종료
메인 스레드는 추가 작업 스레드들을 만들어서 실행시킬 수 있음
메인 스레드가 작업 스레드보다 먼저 종료되더라도 작업 스레드가 계속 실행 중이라면 프로세스는 종료되지 않음
자식을 낳아도 귀속되지않고 따로 행동함
메인스레드 반드시 존재, 메인이 있어야함!

 

작업 스레드

멀티 스레드 프로그램을 개발 시 먼저 몇 개의 작업을 병렬로 실행할지 결정하고 각 작업별로 스레드를 생성

 

Thread 클래스로 직접 생성
java.lang 패키지에 있는 Thread 클래스로부터 작업 스레드 객체를 직접 생성하려면 Runnable 구현
객체를 매개값으로 갖는 생성자를 호출
runnable: 스레드가 작업을 실행할때 사용하는 인터페이스
구현 클래스는 run()을 재정의(overiding)하여 스레드가 실행할 코드를 작성한다.

Thread thread = new Thread(Rnnable target);

 

Thread 자식 클래스로 생성
Thread 클래스를 상속한 다음 run() 메소드를 재정의해서 스레드가 실행할 코드를 작성하고 객체를 생성
혹은 Thread 익명 자식 객체를 사용 가능

thread.setName("스레드 이름");

 

thread하지 않으면 비프음 5번 후 출력 5번 됨, thread 해주면 두개 동시 실행
Runnable 인터페이스: run()

public static void main(String[] args) {
    Thread thread = new Thread(new Runnable() {
      // 자기혼자 동작할 수 있는 독립적인 작업
      @Override
      public void run() { //run 오버라이딩
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        for (int i = 0; i < 5; i++) {
          toolkit.beep();
          try {
            Thread.sleep(1000);
          }catch (Exception e){
          }
        }
      }
    });

    thread.start();

    for (int i = 0; i < 5; i++) {
      System.out.println("삐~");
      try {
        Thread.sleep(500);
      }catch (Exception e){
      }
    }
  }

 

작업 스레드의 이름
작업 스레드 이름을 Thread-n 대신 다른 이름으로 설정하려면 Thread 클래스의 setName() 메소드 사용

 

thread.setName("스레드 이름");

 

디버깅할 때 어떤 스레드가 작업을 하는지 조사하기 위해 주로 사용
어떤 스레드가 실행하고 있는지 확인하려면 정적 메소드인 currentThread()로 스레드 객체의 참조를 얻은 다음 getName() 메소드로 이름을 출력

Thread thread = Thread.currentThread();
System.out.println(thread.getName());

 

.setName으로 main 스레드 이름 변경
subThread이름 0,1,2 이렇게 됨,

public static void main(String[] args) {

    for (int i = 0; i < 3; i++) {
      Thread thread = new Thread() {
        @Override
        public void run() {
//          setName("subThread"); // subThread 이름 설정
          System.out.println(getName()+"실행 중");
        }
      };
      thread.start();
    }

    Thread mainThread = Thread.currentThread();
    mainThread.setName("I am Mainthread");
    System.out.println(mainThread.getName()+" 실행 중");
  }

 

출력

 


메인문과 동시에 작동, 메인문 작업이 끝나도 진행함
자바는 모든 쓰레드에 우선순위 (Priority) 를 기본값 5를 줌
메인문이 만약 다 실행된다면, 자바는 알아서 wait을 시켜버린다. 자식이 종료될 때 까지
정교한 멀티 쓰레드 프로세스는 c++에서나 하자

 

프로세스 = 프로그램 + 프로세스제어블록
프로세스는 독립된 메모리 영역을 할당
자바는 프로세스라는 의미가없다. 다 스레드다, jvm이 관리함, 6개실행(클래스): 6개의 jvm 실행, 끝나면 jvm소멸
thread상태라는 정보 가지고있다. Thread.getState()

 

스레드 라이프사이클:
NEW
A thread that has not yet started is in this state.
RUNNABLE
A thread executing in the Java virtual machine is in this state.
BLOCKED
A thread that is blocked waiting for a monitor lock is in this state.
WAITING
A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
TIMED_WAITING
A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
TERMINATED
A thread that has exited is in this state.

 

스레드 상태

 

실행 대기 상태: 실행을 기다리고 있는 상태
실행 상태: CPU 스케쥴링에 따라 CPU를 점유하고 run() 메소드를 실행. 스케줄링에 의해 다시 실행
대기 상태로 돌아갔다가 다른 스레드가 실행 상태 반복
종료 상태: 실행 상태에서 run() 메소드가 종료되어 실행할 코드 없이 스레드의 실행을 멈춘 상태
new 해서 객체 생성되고 strat해도 바로 run이아니라 runnable상태(실행대기)
cpu 스케줄링에 따라 (일시정지 -> 대기 -> 실행 사이클)

 

 

일시 정지 상태

 

스레드가 실행할 수 없는 상태
스레드가 다시 실행 상태로 가기 위해서는일시 정지 상태에서 실행 대기 상태로 가야야 함
Thread 클래스의 sleep() 메소드: 실행 중인 스레드를 일정 시간 멈추게 함
매개값 단위는 밀리세컨드(1/1000)
sleep join wait (실행 -> 정지)
interrupt notify notifyall (정지 -> 대기)
yield (실행 -> 대기) 급해!! 잠시 양보

 


다른 스레드에게 실행 양보

 

yield() 메소드: 실행되는 스레드는 실행 대기 상태로 돌아가고, 다른 스레드가 실행되도록 양보
무의미한 반복을 막아 프로그램 성능 향상

잘외울것!
일시정지 -> 러너블
interrupt, 예외사항만들어서 복구하거나 스레드 종료
notify wait되어있는 스레드 풀어주는 기능
notifyall wait되어있는 모든 스레드 풀어주는 기능
러닝 -> 일시정지
join 메소드가 종료될때 일시정지 풀림
wait 동기화 블록에서 일시정지, notify해줘야 러너블로 감
sleep시간만큼 일시정지, 자동으로 러너블로 감

 

프로세스 상태 전이
프로세스가 실행하는 동안 상태가 OS에 의해 변경되는 것

 

운영체제
프로세스의 상태를 감시(monitering)
프로세스 상태를 기반 프로세스 스케줄링
프로세스를 관리하고 제어한다.

 

생성(new)---(Admitted)--->준비(Ready): 프로세스 생성을 승인받음
준비(Ready,Runnable) ----Dispatch----> 실행(Running): 준비 상태에 있는 여러 프로세스 중 하나가 스케줄러에 의해 실행
실행(Running) ----interrupt---> Ready,Runnable: Timeout, 이벤트가 발생해서 현재 실행중인 프로세스(스레드)가 준비상태로 전환되는 것
실행(Running) --->waiting : 실행중인 프로세스가 입출력이나 이벤트에 의해 대기해야하는 경우, 입출력이나 이벤트가 끝날때 까지 대기해야 하는 경우
waiting --> Ready : 다시 준비 상태로 만들어서 스케줄러에 의해 선택될 수 있도록 상태전환

 

start가 해당되는 스레드의 새로운 stack을 만듦 -> run 메소드 호출
run종료되면 스택 사라짐
한번쓴스레드는 다시 못씀, new다시해줘야함 (t1.start() 두번 못함)

 

 

두개 start해줘서 동시에 실행된다.

package javaStudy.threadex;

public class ThreadEx1 {
  public static void main(String[] args) {
    //1. 상속으로 Thread 생성
    ThreadByInheritance t1 = new ThreadByInheritance();

    //2. Runnable Interface 로 Thread 생성
    Runnable runnable = new ThreadByImplement();
    Thread t2 = new Thread(runnable);
    Thread t3 = new Thread(new ThreadByImplement());

    t1.start();
    t2.start();
    //t1 t2끝나고 메인종료
  }
}

class ThreadByInheritance extends Thread{
  //thread 상속
  @Override
  public void run() { // 오버라이딩
    for (int i = 0; i < 500; i++) {
      System.out.print(0);
    }
  }
}

class ThreadByImplement implements Runnable{
  //Runnable 상속
  @Override
  public void run() {
    for (int i = 0; i < 500; i++) {
      System.out.print(1);
    }
  }
}

 

결과

 

print 하면 실행중인 스레드의 참조값 반환

public class ThreadDemonEx {
  public static void main(String[] args) {
    Thread t1 = Thread.currentThread();
    System.out.println("currentThread = "+t1);
    //현재 실행중인 스레드의 참조값을 반환하여 출력

    //ThreadEx11스레드 t2를 만들어서 t2의 반환값 확인
    Thread t2 = new Thread(new ThreadEx11());
    System.out.println("t2 :" + t2);
  }
}
//ThreadEx1 클래스를 thread로 만드세요
class ThreadEx1 implements Runnable{
  @Override
  public void run() {

  }
}
class ThreadEx11 implements Runnable{
  @Override
  public void run(){

  }
}

 

숫자 출력 스레드를 먼저 시작했기 때문에 먼저 시작됨
숫자 출력되면서, 입력받는 창이 뜬다.

public class ThreadDemo1 {
  public static void main(String[] args) {
    Thread t1 = new Thread(new CountDownThread());
    t1.start();

    //사용자 입력
    String name = JOptionPane.showInputDialog("이름을 입력하세요!");
    System.out.println("이름은 " + name + "입니다.");


  }
}
class CountDownThread implements Runnable {

  @Override
  public void run() {
    for (int i = 10; i > 0; i--) {
      System.out.println(i);
    }
  }
}

 

결과

 

Thread 선언하기:

Thread t1 = new Thread(new ThreadE)); -> t1.start
Runnable runnable = new ThreadE();
    Thread t1 = new Thread(runnable);

 

sleep(long millis) : 지정된 시간동안 스레드를 멈춘다. static method이기 때문에
자기 이름 가져올 때: Thread.currentThread().getName()

class ThreadE implements Runnable{
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName());
    for (int i = 0; i < 20; i++) {
      System.out.println("*");
      try {
        Thread.sleep(2000);
      }catch (Exception e){}
    }
    System.out.println("["+Thread.currentThread().getName()+","+"종료]");
  }
}

 

interupt 메소드:
스레드가 일시 정지 상태에 있을 때 InterruptedException 예외 발생
예외 처리를 통해 run() 메소드를 정상 종료

 

Thread의 interrupted()와 isInterrupted() 메소드는 interrupt() 메소드 호출 여부를 리턴

 

 

isInterrupted() : true / false 인지 반환, true면 멈춤??

  public static void main(String[] args) {
    Thread t1 = new Thread(new ThreadE());
    t1.start();
    
    System.out.println("isInterrupted : "+t1.isInterrupted());

    t1.interrupt(); 
    System.out.println("isInterrupted : "+t1.isInterrupted());

  }

 

동기화 메소드와 블록
스레드 작업이 끝날 때까지 객체에 잠금rock을 걸어 스레드가 사용 중인 객체를 다른 스레드가 변경할수 없게 함 = synchronize
스레드 B는 동기화블록과 동기화 메소드는 사용할 수 없다.

 

 

동기화 메소드 및 블록 선언
인스턴스와 정적 메소드에 synchronized 키워드 붙임
동기화 메소드를 실행 즉시 객체는 잠금이 일어나고, 메소드 실행이 끝나면 잠금 풀림
메소드 일부 영역 실행 시 객체 잠금을 걸고 싶다면 동기화 블록을 만듦

메소드에 synchronized걸려있으니까 스레드하나가 접근하고 있으면 코드의 2초동안 아무도 접근 못함,
그래서 서로 충돌안하고 user1 의 100 이 출력되고, 2초뒤 user2의 50이 출력됨


Calculator class

public class Calculator {
    private  int memory;

    public int getMemory(){
        return  this.memory;
    }
    public synchronized void setMemory1(int memory){
           this.memory = memory;
           try{
               Thread.sleep(2000);
               System.out.println(Thread.currentThread().getName() + " : " + "현재금액"+ this.memory);
           }catch (InterruptedException e){
               System.out.println(Thread.currentThread().getName() + " : " + "현재금액"+ this.memory);
           }
    }
    public  void setMemory2(int memory){
        synchronized (this) {
            this.memory = memory;
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + " : " + "현재금액"+ this.memory);
            }
            catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " : " + "현재금액" + this.memory);
            }
        }
    }

 

Uer1Thread class

public class Uer1Thread extends  Thread{
             private Calculator calculator;

             public Uer1Thread(){
                 setName("User1Thread");
             }

             public void setCalculator(Calculator calculator){
                 this.calculator = calculator;
             }

    @Override
    public void run() {
         calculator.setMemory1(100);   //동기화 메소드 호출
    }
}

 

Uer2Thread class

public class Uer2Thread extends  Thread{
             private Calculator calculator;

             public Uer2Thread(){
                 setName("User2Thread");
             }

             public void setCalculator(Calculator calculator){
                 this.calculator = calculator;
             }

    @Override
    public void run() {
         calculator.setMemory2(50);   //동기화 메소드 호출
    }
}

 

SynchronizedMain class

public class SynchronizedMain {
    public static void main(String[] args) {
         Calculator calculator = new Calculator();

         Uer1Thread user1 = new Uer1Thread();
         user1.setCalculator(calculator);
         user1.start();

        Uer2Thread user2 = new Uer2Thread();
        user2.setCalculator(calculator);
        user2.start();
    }
}

 

결과

 

 

회고

 

과거 자바공부할때 스레드를 잠깐 공부했어서 간단히 알고 있었다. OS를 공부할 때 멀티 스레드를 접해서 자세히 알고 싶었는데 이번 기회에 배울 수 있어서 좋았다.
하지만 생각보다 메소드도 많고 , 신경써줘야할 부분들이 있다. 복습을 열심히할 필요성이 있다.

 

오늘 시험쳤다. 90점이면 준수한거같다. 자바과정이 며칠뒤면 끝나는데 다음과목도 점수잘받고싶다. 화이팅하자 .

반응형