달력

12

« 2024/12 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

저번에 멀티쓰레드와 멀티프로세스에 관해서 설명한적이 있는데 

이번에는 싱글쓰레드와 멀티쓰레드에 관해 다뤄보려고한다.


그전에 컨텍스 스위칭에대해 알고넘어가사 


컨텍스트 스위칭(context switching)이란 프로세스 또는 쓰레드 간의 작업 전환을 말한다. 


<가정을 해보자> 

두개의 작업을 

-하나의 쓰레드(t1)로 처리하는 경우 

-두 개의 쓰레드로 처리하는경우(t1,t2)


t1으로 두 작업을 처리하는 경우는 한 작업을 마친후에 다른 작업을 시작한다.

t1,t2으로 작업하는 경우 짧은시간동안 2개의 쓰레드가 번갈아가며 작업을 수행해서 동시에 두작업이 처리되는 것과 같이 느끼게한다.


싱글코어에서 싱글쓰레드 프로세스와 멀티쓰레드 프로세스를 비교하면 


두 경우다 작업을 수행한 시간은 거의 같다. 

오히려 두 개의 쓰레드로 작업한 시간이 싱글쓰레드로 작업한 시간보다 더 걸리는데 

그 이유는 

쓰레드간 작업전환(context switching)에 시간이 걸리기 때문이다. 

작업전환을 할대는 현재 진행 중인 작업의 상태등을 저장하고 읽어 오는 시간이 소요된다. 

(쓰레드 스위칭에 비해 프로세스 스위칭이 더 많은 정보 저장해야하므로 더 많은 시간이 소요됨!)


그래서 싱글코어에서 단순 cpu만을 사용하는 계산작업이라면 오히려 멀티쓰레드보다 싱글쓰레드로 프로그래밍 하는게 더 효율적이다.


두 개의 쓰레드로 작업하는데 더 많은 시간이 걸린 이유는 두가지다.

(1)두 쓰레드가 번갈아가며 작업을 처리하기에 쓰레드간 작업전환시간이 소요됨

(2)한 쓰레드가 화면에 출력하고 있는 동안 다른 쓰레드는 출력이 끝나기 기다려야하는데 이때 발생하는 대기시간 때문이다.



여러 쓰레드가 여러 작업을 동시에 진행하는 것을 병행(concurrent)라고 하고, 

하나의 작업을 여러 쓰레드가 나눠서 처리하는 것을 병렬(parallel)이라고 한다. 




1.싱글코어로 두 개의 쓰레드를 실행하는 경우와 

2.멀티코어로 두개의 쓰레드를 실행하는 경우에도 차이가 발생한다.


1의 경우는 멀티쓰레드라도 하나의 코어가 번갈아가며 작업을 수행하는 거라 두 작업이 절대 겹치지 않는다.

2의 경우는 멀티쓰레드로 두 작업을 수행하면, 동시에 두 쓰레드가 수행될 수 있으므로 

               두 작업, A와B가 겹치는 부분이 발생한다. 

--> 그래서 화면(console)이라는 자원을 놓고 두쓰레드가 경쟁하게된다. 

 

하지만 이런 결과는 실행할때마다 다른 결과를 얻을 수 있다. 

왜냐면 내가 실행하고있는 예제프로그램(프로세스)이 OS의 프로세스 스케줄러의 영향을 받기 때문이다. 


JVM이 쓰레드 스케줄러에 의해 어떤 쓰레드가 얼마동안 실행될건지 결정하는거와 같이 

프로세스도 프로세스 스케줄러에 의해서 실행순서와 실행시간이 결정되기 때문에 


매 순간 상황에 따라 프로세스 할당되는 실행시간이 일정치않고, 쓰레드에게 할당되는 시간 도한 일정하지 않다. 

--> 그래서 쓰레드가 이런 불확실성을 가지고 있다는 것을 염두에 둬야한다.


자바가 OS에 독립적이라곤 하지만 실제로는 OS종속적인 부분이 몇가지 있는데 쓰레드도 그 중의 하나이다.

(JVM종류에 따라 쓰레드 스케줄러의 구현 방법도 다를수 있다.)


..그림 나중에 첨부해야지!! 



두 쓰레드가 서로 다른 자원 사용하는 작업의 경우는 싱글쓰레드 프로세스보다 멅이쓰레드 프로세스가 더 효율적이다. 


ex)) 사용자로부터 데이터 입력받는 작업, 네트워크 파일 주고받는 작업, 프린터로 파일 출력하는 작업 

-->외부기기와의 입출력을 필요로 하는경우 


(A)사용자로부터 입력받는 작업

(B)화면에 출력하는 작업 


이 두가지를 하나의 쓰레드로 처리하면 사용자가 입력을 마칠 때까지 아무 일도 하지 못하고 기다려야한다. 

그러나 두 개의 쓰레드로 처리하면 사용자 입력 기다리는 동안 다른 쓰레드가 작업을 처리할 수 잇기 때문에 보다 효율적인 cpu사용이 가능하다.



:
Posted by Gongdile
2018. 5. 16. 14:12

글쓰면서 느낀거 개인/생각 정리2018. 5. 16. 14:12

공부목적으로 블로그를 작성한거라 

공부할일이 있거나 시간을 내서 정리하는 편인데 

책이나 피피티등 자료에서 충분히 나오는 부분을 굳이 세세하게 설명할 필요가 없다고 느꼈다.

물론 내가 모르겠어서 세세하게 풀어야한다면 의미가 있겟지만


블로그에 정리하는거니까 더  자세히 자세히 ! 막 이럴 필요는 없을거같다 

그러면 흥미도 좀 사라질거같아서 

순서가 뒤죽박죽이거나 빠지는 부분이 있더라도 내가 시간을 내서 생각하고 정리한거에 의미를두고 

시간 날때마다 글쓴거 보면서 추가하고 빼는게 더 효율적인거같다.


뭔가 완벽하게 빼먹지말고 잘적어봐야겠어 라는 욕심이 요새 올라왓다 

그게 나쁜건 아니지만 블로그외에도 우선 순위라는게 있으니까 


너무 힘빼지말자 !


전체적으로 훅쓰고 나중에 정리해도 괜찮은거같다

보고 또보면 되지뭐 어디 제출해야해서 모든걸 다써야하는것도 아니고 

 글이 어딘가로 슉! 날라가는것도 아니고 


꾸준함이 중요하니까 잘 기록해서 지식으로 남기를!  

너무 무리하지말기!_! 



'개인 > 생각 정리' 카테고리의 다른 글

몰입과 정리  (1) 2023.02.04
꾸준히  (0) 2021.07.22
되돌아보기  (0) 2020.03.04
오늘의 일지  (0) 2018.01.22
C언어 공부 //JAVA 2의보수  (0) 2017.12.04
:
Posted by Gongdile

]저번 포스팅에서 쓰레드(Thread)가 무엇인지에 관해서 설명했다. 

 [JAVA]Thread(쓰레드)- 1.Thread란 무엇일까? 정의,멀티쓰레드와 멀티프로세스

 -->http://gongstudyit.tistory.com/archive/20180515



이번에는 쓰레드를 구현하고 실행하는 방법 그외에 쓰레드의 우선순위, 실행주기등에 대해서 설명하려고한다.


자바에서 스레들르 만들기 위해서는 두가지 작업을 해야한다.

  • 스레드 코드 작성
  • JVM에게 스레드를 생성하여 스레드 코드를 실행 하도록 요청 
--> 자바에서 스레드를 만드는 방법에는 두가지가 있다.

1. Thread 클래스 상속
2. Runnable 인터페이스 구현 

어느 쪽을 선택해도 별 차이는 없지만 
쓰레드 클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에 (자바에서는 단일상속만 허용! )
Runnable인터페이스 구현이 일반적이다. 

// 1. Thread클래스를 상속
class MyThread extends Thread{
   public void run() {/* 작업내용   */ }  //Thread클래스의 run( ) 을 오버라이딩
}
// 2. Runnable클래스를 상속
class MyThread implements Runnable{
  public void run() {/*  작업내용 */ } //Runnable인터페이스의 추상메서드 run()을 구현 
}

Runnable 인터페이스는 오로지 run( ) 만 구현되어있는 간단한 인터페이스라서 

구현하기 위해서는 추상메서드인 run의 몸통 { } 을 만들어주면 된다. 


쓰레드 구현하는 2가지 방법의 차이는 

Thread 클래스를 상속받은 경우와 Runnable 인터페이스를 구현한 경우의 인스턴스 생성 방법이 다르다.


아래 예제를 보자 

public class ThreadEx1 {

	public static void main(String[] args) {
		ThreadEx1_1 t1 = new ThreadEx1_1(); //Thread의 자손 클래스 인스턴스 생성
		
		Runnable r = new ThreadEx1_2(); //Runnable을 구현한 클래스의 인스턴스 생성
		Thread t2 = new Thread(r); //생성자 Thread(Runnable target)
		
  //저위의 두줄을 한줄로 간단하게 하려면..r 대신에 new ThreadEx1_2()를 넣으면 된다. 
	        t1.start();
		t2.start();
	}

}
class ThreadEx1_1 extends Thread{
	public void run() {
		for(int i=0; i<3; i++) {
			System.out.println(getName());//조상인 Thread의 getName()을 호출 
		}
	}
}

class ThreadEx1_2 implements Runnable{
	public void run() {
		for(int i=0; i<3; i++) {
			//Thread.currentThread() - 현재 실행중인 Thread를 반환한다. 
			System.out.println(Thread.currentThread().getName());//조상인 Thread의 getName()을 호출 
		}
	}
}
<실행결과>
Thread-0
Thread-0
Thread-0
Thread-1
Thread-1
Thread-1

Runnable인터페이스를 구현한경우, 구현한 클래스의 인스턴스를 생성한 다음에 (위의 r처럼)

이 인스턴스를 Thread클래스의  생성자의 매개변수로 제공해야한다. 


Thread클래스를 상속받으면, 자손 클래스에서 조상인 쓰레드 클레스의 메소드를 직접 호출할수있지만.

Runnable을 구현하면 쓰레드 클래스의 static 메서드인 current Thread( )를 호출하여 쓰레드에 대한 참조를 얻어와야만

호출이 가능하다. 


그래서 위의 예제에 ThreadEx1_2에는 멤버라고는 run( )밖에 없기 때문에

 'Thread.currentThread().getName()'와 같이 해야한다.


  • static Thread currentThread( ) 현재 실행중인 쓰레드의 참조를 반환한다.
  • String getName( )                 쓰레드의 이름을 반환한다. 


<쓰레드의 실행 - start( ) >

start( ) 를 호출해야만 쓰레드가 실행된다. 
start( ) 를 호출했다고 바로 실행되는건 아니고 실행대기에 있다가 자신의 차례가 되어야 실행된다.!

여기서 알아둬야할건 한번 실행이 종료된 쓰레드는 다시 실행할수없다.
즉, 하나의 쓰레드에 대해 start( )가 한 번만 호출될 수 있다. 
그래서 내가 만약 쓰레드의 작업을 한번 더 수행해야 한다면 
 --> 새로운 쓰레드를 생성한다음에 start( ) 를 호출해야한다.

하나의 쓰레드에 대해 start( )를 두번 이상 호출하면 실행시에 IllegalThreadStateException이 발생한다. 

이렇게 에러가 발생!! 


이렇게 만들어줘야한다 

그리고 쓰레드의 이름을 지정하지 않아서 'Thread-번호'의 형식으로 정해져서 콘솔에 출력된건데. 이거 내가 바꿔줄수있다.

다음과같은 생성자나 메서드를 통해서 지정또는 변경할수있다.

Thread(Runnable target, String name)
Thread(String name)
void setName(String name)

아까 위의 코드에서 System.out.println(Thread.currentThread().getName())은 

아래의 코드를 줄여 쓴 것이라고 이해하면 된다. 
Thread t= Thread.currentThread(); //현재 실행중인 쓰레드의 참조를 반환해서 t에 넣는다.
String name= t.getName(); //쓰레드의 이름을 얻어서 name에 넣는다.
System.out.println(name); //name 을 출력한다. 

이렇게 이용할수있다. 


근데 여기서 주의할거는 쓰레드 1, 쓰레드 2가 돌아간다고 가정할때 1,2의 네임을 똑같이 해주면 

어떤 쓰레드가 돌아가고있는지 네임을 얻어서 콘솔창에서 찍을때 알수가없다


 


그러면 스레드1 스레드2의 이름은 다르게하고 스레드1을 두번 실행했을때는 어떨까??

당연한 얘기지만 Thread_Ex3에서 set해준 네임그대로 찍혀나온다.


결론은 

아까 위에서 한쓰레드 대해 start( )가 한번만 호출될수있는데 

만일 쓰레드 작업을 한번더 수행해야할때 새로운 쓰레드를 만들어서 start( )를 호출했는데


그때 

thread -0

thread -1이라고나온건 내가 쓰레드 이름을 설정해주기 않았기때문에 그렇게 나온거라는걸 얘기하고싶었다. 


맨처음 예제에서 쓰레드를 상속받아 구현한것과 러너블 인터페이스를 구현한것도 출력결과가 

thread-0

thread-1이라고 나왔는데 


이것도 이름을 설정해주지 않아서인데 이름을 설정하게되면 다른스레드는 서로 다른이름으로 찍히겠지만, 

같은 쓰레드는 같은 이름으로 나온다는걸 (혼동할수있을거같아서) 설명했다.



<start( )와 run( )>


코드에 start( ) 와 run( ) 이 자주 등장했는데 둘에대해 설명하려고 한다.


main메서드에서 run( )을 호출하는 것은 생성된 쓰레드를 실행시키는게 아니라 

                                                     -->단순히 클래스에 선언된 메서드를 호출하는거다.


반면에 start( )는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택(call stack)을 생성한 다음에 

                                                     -->run( )을 호출해서, 생성된 호출스택에 run( )이 첫번째로 올라가게 한다.


뭔말이냐?? 면..! 


맨위에 예시처럼 우리가 스레드를 상속받아서 구현시에 run( ) 이라는 메소드를 오버라이딩 했는데 

run( )을 스레드코드 라고부른다. 

스레드는 run( ) 메소드에서부터 실행을 시작하고 run( ) 이 종료하면 스레드도 종료한다.

-->(만약 run( )을 오버라이딩 하지 않으면) Thread클래스의 run( )이 실행되며 Thread의 run( )은 아무일도 하지x 

    단순 리턴하도록 작성되어 있어서 스레드가 바로 종료된다. 


그러니까 쓰레드를 상속받아서 run()이라는 메소드를 오버라이딩해서 스레드 코드를 작성한다. 

그리고 스레드 객체를 생성한거에 생명력을 가지고 실행하게 하는게 바로  start다 !! 



start( )메소드를 호출함으로써 스레드를 동작시킨다. 

(start( )메소드는 Thread클래스에 구현된 메소드이며 개발자가 오버라이딩하면 안된다.)


start( )메소드가 생성된 스레드 객체를 스케줄링이 가능한 상태로 전환하도록 JVM에게 지시를 한다. 

이후 스케줄링에 의해서 그 스레드가 선택되면 비로소 JVM에 의해 run( )메소드가 호출되어 실행을 시작한다.!! 


:
Posted by Gongdile