Thread1

제이·2023년 3월 13일
post-thumbnail

Thread

프로세스란?

프로세스는 실행중인 프로그램을 의미한다.
운영체제로부터 자원을 할당받는다.

  • A와 B가 있다고 생각해보자. A의 작업이 끝나지 않았을 때 아무것도 할 수 없으며 B작업도 할 수 없다. 그래서 <시분할방식>을 적용하게 된다.

  • 시분할방식은 시간을 쪼개서 하는 개념으로, 사용자는 2개가 동시에 시작되고 작동된다고 느낀다.

  • <자원>은 cpu, ram, 시간 등을 일컫는데, 이런 자원을 할당해주는 것이 <o/s>다.
    이 o/s가 이만큼씩 하라고 명령하는데(a들어가, b들어가) 이것을 <스케줄링>이라고 한다.

  • o/s는 컴퓨터에 있는 자원을 컨트롤하는 것이다. 규칙적이지 않고, 언제,얼만큼 자원을 할당받게 될 지 예측할 수 없다. 동시에 여러가지 일들이 진행되는 것(음악들으면서, 인강듣고, 과제하는..)을 멀티태스킹이라고도 하는데, 사실 동시는 아니다. 한 가지 시점에 일어나는 일은 하나밖에 없다.

  • 2개의 작업이 동시에 일어나는 것 : 병행과 병렬

  • 병행 : 위의 설명. 실제로는 아니지만 우리가 동시에 일어난다고 느끼는 것. 소프트웨어적.

  • 병렬 : cpu의 코어들. 코어가 2개인 것을 듀얼코어라고 하는데, 실제로 작업이 2개가 일어나는 것이다. 하드웨어적.
    tip)코어수가 많아지면 작업량이 많아진다. cpu 에서 속도를 결정하는 것은 클럭과 배수율같은거..

스레드란?

프로세스가 할당받은 자원을 이용하는 실행단위이다.

  • o/s가 프로그램을 선점했을 때, 이때 그 선택받은 시간 한칸. 저 시간안에서도 동시에 느끼게 해주는 프로그램이 있는데 그게 Thread!
    즉, 프로세스 내에서 실행되는 여러 흐름의 단위이다.
    시간 안에서도 여러가지 작업들이 있는데 거기 안에도 스케줄링이 일어난다.

✍혼자팁! 프로세스는 자원을 공유하지않지만, 스레드는 자원을 공유한다.

프로그램 -> 프로세스 -> 스레드

자바스레드

  • 자바프로그램안에서 동시에 한다고 느끼게끔 해주는 일은 JVM이다.
    스케줄링의 주체가 가상머신이다. 즉, o/s역할을 JVM이 한다.
    o/s(프로세스차원)로 하여금 가상머신한테 자원을 할당시키면, JVM(프로세스내부)이 들과 와서 다히 할당을 해준다. 여기서 기능 하나하나를 Thread라고 한다.
    프로세스 하나는 thread1 +thread2. thread가 여러개 모여 프로세스가 된다.

스레드를 배운 후

  • 쓰레드를 배운 후부터는 프로그램 맥락이 여러개가 될 수 있다.
    순서를 예측할 수 없고, 작업순서를 예측할 수 없다.
    흐름이 하나인 프로그램 -> 단일프로그램
    main이 여러개 있는 프로그램 -> 쓰레드가 있는 프로그램.
    main 그 자체가 thread이다.

Thread를 사용하는 방식1 - 상속

//예시1
class ExtendThread extends Thread {	
//스레드를 상속받는 방식 -> 상속은 하나밖에 못받음.
	@Override
	public void run() {
//		System.out.println("Thread 클래스를 상속:" + getName());	//extends thread의 이름을 가져온다. 
		System.out.println("Thread 클래스를 상속:" + Thread.currentThread().getName());
	}
}
public class ExtendThreadTest {
	public static void main(String[] args) {
		ExtendThread et = new ExtendThread();
		//start()를 이용하여 스레드를 시작시킨다.
		//이후 ExtendThread의 run()가 실행되고 
        //run()가 종료되면 바로 ExtendThread가 소멸된다.
		et.start();
        //et.run(); 
        //run()을 하면, 활성화가 안된거. 동작하지 않는다. 
        //start()하지 않았기 때문에.
		System.out.println("end of main : "+ Thread.currentThread().getName());
	}
}
//
//결과 
end of main : main
Thread 클래스를 상속:Thread-0(start()하면서 run()이 작동되니까.)
----------------------------------------------------------------------
//예시2
class MyThread extends Thread {
	@Override
	public void run() {
		String name = Thread.currentThread().getName();
		for(int i=0; i <5; i++ ) {
			System.out.println(name);
		}
	}
}
public class ThreadEx1 {	
	public static void main(String[] args) {
		MyThread t = new MyThread();
		t.start();
		String name = Thread.currentThread().getName();
		for(int i = 0; i<5; i++) {
			System.out.println(name);
		}
        //선택됐다가 다시 돌아갔다가 다시 선택될 수 있음. 
        //교대로 작업이 일어난다. 흐름이 2개가 생겼다. 
        //main에서 start()를 적으면 main과 함께 thread가 생성된다.
        //start()하는 순간부터 mythread랑 thread랑 경쟁을 하게 된다. 
        //그때그때마다 바뀐다. 순서가 할때마다 바뀐다. 
	}
}
//결과
main
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
main
main
main
main
  • main thread가 et.start()를 실행함.
  • Thread.currentTread() : 이 구문을 수행하고 있는(현재작업하고 있는)스레드를 가져와라

Thread를 사용하는 방식2 - Runnable

class ThreadEx1_1 extends Thread {
	public void run() {
		for(int i= 0 ; i <5 ; i++) {
			System.out.println("t1 : "+ Thread.currentThread().getName());
		}
	}
}
class ThreadEx1_2 implements Runnable {
	public void run( ) {
		for(int i = 0 ; i < 5 ; i++) {
			System.out.println("t2 : "+Thread.currentThread().getName());
		}
	}
}
public class CreateThread {
	public static void main(String[] args) {
		ThreadEx1_1 t1 = new ThreadEx1_1();
        `
		Runnable r = new ThreadEx1_2(); //스레드 아니다!!!!!!!!
		Thread t2 = new Thread(r);
        `
		t1.start();
		t2.start();
		//main or t1 or t2가 경쟁한다. 
        //t1이 먼저 실행된다고 해서 먼저 작동되는 것은 아니다. 
        //서로 경쟁하는 것이기 때문에. 
		//start() 하는 순간부터 경쟁. main이 먼저 나온다고 볼 수 없다. 
	}
}
//결과
t2 : Thread-1
t2 : Thread-1
t2 : Thread-1
t2 : Thread-1
t2 : Thread-1
t1 : Thread-0
t1 : Thread-0
t1 : Thread-0
t1 : Thread-0
t1 : Thread-0

✍위의 코드 설명!!

  • ThreadEx1_1 : 상속받음
    ThreadEx1_2 : Runnable
  • 상속받은 Ex1_1은 바로 Thread생성함.
    Runnable한 Ex1_2는 먼저 Runnable만들어주고, 그걸 Thread에 넣어준다.
  • 여기서는 main, t1 ,t2(3개)가 서로 경쟁한다.
    start()하는 순간부터 경쟁이기때문에, 먼저 나오는 것이 main인지, t1인지, t2인지 알 수 없다.

start(), run(), main

  • run()을 오버라이드 받아서 사용한다.
    이때 run()은 thread가 실제로 해야할 일을 적어놓은 것.
    그런 후 start()를 해줘야 thread가 생성이 된다!!

  • 가상머신이 main을 사용할지 쓰레드를 사용할지 모른다.
    start()되는 순간 thread가 main의 경쟁자가 된다.
    main도 한개의 Thread로 취급한다.
    사실상 main은 run()과 같다-> 가상머신이 부른다. 우리가 start()를 부르지 run()과 main을 부르지 않는다.
    et.start()하는 순간 main과 start()가 경쟁자가 된다. 그전에는 main이 독점.

  • thread클래스를 상속받는다.-> 걔한테 뭔가 시킬일이 있다. run()이라는 메소드. 그안에 쓰레드가 실제로 해야되는 일을 활성화시킨다.
    먜 ) run()만 있을 경우에는 작동이 안되는 느낌.

Thread 예시

//4
public class NormalProcess {
	public static void main(String[] args) throws Exception{
		//사용자 입력이 있을때까지 BLOKED(thread 작동하지 않는다.)
		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) {
			}
		}
	}
}
//결과
입력하신 값은3입니다
10
9
8
7
6
5
4
3
2
1
`
//5
//4은 일을 두가지로 나눠놨다.
//스레드를 사용해기때문에 입력창이 떠있음에도 count가 시작되고 있다. 
class ThreadEx extends Thread {
	public void run() {
		for(int i=10; i >0 ; i--) {
			System.out.println(i);
			try {
				sleep(1000);// timed waiting으로 빠지게 된다. 
                //시간이 지나면 깬다.(runnable로 간다.)
			}catch(Exception e) {
			}
		}
	}
}
public class UsingThread {
	public static void main(String[] args) throws Exception {
		ThreadEx th1 =  new ThreadEx();
		th1. start();
		String input = JOptionPane.showInputDialog("아무값이나 입력하세용");
		System.out.println("입력하신 값은"+input +"입니다.");
	}
}
//결과
10
9
8
7
6
입력하신 값은3입니다.
5
4
3
2
1

//4는 start()가 없기 때문에 thread가 main뿐이다. 그래서 main안에서 순서대로 실행하기 때문에 입력하지않으면, I/O Blocking으로 인해서 run -> not runnable로 간다. 그래서 그 다음코드가 실행되지 않는 상태가 된다. 이제 값을 입력하면 그다음 코드가 실행된다.
`
//5는 start()가 있기 때문에 th1과 main이 함께 경쟁이 된다. 그래서
두개가 동시에 진행된다.

Thread Lifecycle

  • runnable : 실행할 수 있는 상태 - 여기에 있어야 실행이 가능하다. main이 실행이 되면서 t1이 생성(new)된다.
    t1.start()를 시키면 ruunable에 온다. 간택받은 상태로 된다.
  • run : 실행하고 있는 상태. main이 이미 실행(run에 있으면)되고 있으면 t1이 runnable에서 기다리고 있다. 그리고 main이 실행시간이 끝났으면, 다시 runnable로 main이 돌아가고 그러면 main과 t1이 둘 다 대기를 타게 됨(runnable에 있게 됨).
    `
  • 💡Thread t = new Thread(); 되면!!! 위의 그림에서 "생성"이 된 거!!
  • 💡t.start() 된 순간 runnable로 들어가게 된다!!!!!
  • main이 run에서 notRunnable로 가게 되는 경우 3가지가 있다.
    1.BLOCKED (I/O Blocking)
    2.TIMED_WAITING (wait(), sleep(), join() with timeout)
    3.WAITING (join() with no timeout)
    `
  • 4코드를 본다면, 사용자가 입력을 하면, not runnable에 있던 main이 탈출한다. 그래서 그 다음이 작동할 수 있다.
    `
  • 프로그램종료
    만약 main이 끝났다 -> 스레드가 끝났다면, dead로 빠진다. 이건 재활용할 수 없다. 다시 똑같은 기능을 사용하고 싶다면, 새롭게 만들어야 한다.
    모든 스레드가 죽으면 이러면 진짜 프로그램종료가 된다.
    스레드마다 callstack이 존재한다. 원래 main하나에만 있어서 그냥 main이 죽으면 프로그램이 끝이엇는데, thread마다 callstack이 존재해서 모든 스레드가 종료되어야만 프로그램이 모두 종료된다고 할 수 있다.
    즉, 모든 스레드의 콜스택이 다 비워졌을 때 비로소 프로그램이 종료되었다고 볼 수 있다.
    스레드가 종료되었을 때는 자기할 일이 끝났을 때는 terminated(Dead)된다.
    단일스레드일 때, 예외가 발생할 때는 프로그램이 멈춘다.

03.13일 수업

profile
Hello :)

0개의 댓글