Thread : a light-weighted process
Process : a running program ( 동작하는 프로그램 )
Program : executable file ( 실행 가능한 파일 )
public class Test203 { public static void main( String[] args ) { Runnable r = new Runnable(){ public void run() { System.out.println("HelloThread"); } }; Thread t = new Thread( r ); t.start(); } }
Thread 를 생성하는 방법
Runnable
인터페이스를 상속받은 클래스의 인스턴스를 생성한다.public void run()
을 오버라이딩 한다.생성자
에 넣어준다. ( Thread 생성 )public class Test204 { public static void main( String[] args ) { StringBuffer sb = new StringBuffer(); Runnable r = new Runnable(){ public void run() { for( int i = 0 ; i < 100 ; i++ ) { try { Thread.sleep( 350 ); } catch( InterruptedException e ) {} System.out.println("run " + i ); sb.append('*'); } System.out.println("run end " + sb.toString() ); } }; Thread t = new Thread( r ); t.start(); // for( int i = 0 ; i < 100 ; i++ ) { try { Thread.sleep( 250 ); } catch( InterruptedException e ){} System.out.println("main " + i ); sb.append('#'); } System.out.println("main end"); } }
실행결과 >> main 0 run 0 main 1 run 1 main 2 main 3 run 2 main 4 run 3 main 5 run 4 main 6 main 7 run 5 main 8 run 6 main 9 main 10
sleep( int t ) throws InterruptedException
Thread.sleep( 500 ); -- 0.5 초 동안 잠시 멈추어선다
스레드
하나의 프로세스 안에서 독자적으로 동작하는 가벼운 프로세스를 생성하는 것이 쓰레드의 개념이 된다.
쓰레드를 생성하고 돌리는 프로그램에서는 main 이 끝났다고 해서 끝난게 아니라 모든 쓰레드가 끝이 나야 프로그램은 종료된다.
하나의 프로세스 안에서 생성된 쓰레드들 사이에서는 메모리 공유가 가능하다.
// 스레드를 생성하고 이용하는 두번째 방법 class Kim extends Thread { public void run() { System.out.println("HelloWorld"); } } // public class Test205 { public static void main( String[] args ) { Thread t = new Kim(); t.start(); } }
Runnable 은 생각하지 않고 스레드를 상속받은 클래서에서 run() 을 오버라이딩 해준다. start() 를 호출하면 OS 로 부터 스레드가 할당되어 run() 을 동작시킨다.
스레드는 물리적인 CPU 를 소프트웨어적으로 분산을 만드는 개념과 같다. 이들은 독자적, 독립적으로 구동되며( 동시 실행가능), 메모리를 공유한다.
3개로 나누면 1/3 으로 힘이 분산되는데 왜 사용하는걸까??
어떤 하나가 서비스를 길게 받는 일이 생긴다면 다른 것들은 우회할 수 있어야 한다. 서비스를 제공하는 서버 프로그램
에서는 스레드 개념이 필수적이다.
class Toilet { public void bigWork( String by ) { System.out.println("BW STEP 1 : " + by ); System.out.println("BW STEP 2 : " + by ); System.out.println("BW STEP 3 : " + by ); System.out.println("BW STEP 4 : " + by ); System.out.println("BW STEP 5 : " + by ); } } class Kim extends Thread { private Toilet toi = null; public Kim( Toilet t ) { this.toi = t; } public void run() { for( int i = 0 ; i < 10 ; i++ ) { toi.bigWork("kim"); } } } class Park extends Thread { private Toilet toi = null; public Park( Toilet t ) { this.toi = t; } public void run() { for( int i = 0 ; i < 10 ; i++ ) { toi.bigWork("park"); } } } public class Test206 { public static void main( String[] args ) { Toilet toilet = new Toilet(); Thread k = new Kim( toilet ); Thread p = new Park( toilet ); k.start(); p.start(); } }
살행결과>> BW STEP 1 : Kim BW STEP 1 : Park BW STEP 2 : Park BW STEP 3 : Park BW STEP 4 : Park BW STEP 5 : Park BW STEP 1 : Park BW STEP 2 : Park BW STEP 3 : Park BW STEP 4 : Park BW STEP 5 : Park
여러 스레드가 동시에 하나의 인스턴스를 사용하는 경우에 같은 칸에서 동작하는 일이 발생!
위의 코드를 예로 들자면 화장실 칸이 5개가 있다. 그런데 Kim 과 Park 이 같은 화장실 칸에 들어가서 동시에 일을 보겠다고 하는 상황과 같다.
해결 방법 : 모두가 각각 자신의 전용 화장실을 가지고 있으면 이런 일이 안 생기지만 현실적으로는 불가능하다. 따라서 이런 경우에는 먼저 사용하는 쓰레드가 문을 잠그고 들어가면 그 다음 쓰레드는 기다리도록 한다. ( 동기화
개념 )
class Kim extends Thread { private Toilet toi = null; public Kim( Toilet t ) { this.toi = t; } public void run() { for( int i = 0 ; i < 10 ; i++ ) { synchronized( toi ) { toi.bigWork("kim"); } } } } class Park extends Thread { private Toilet toi = null; public Park( Toilet t ) { this.toi = t; } public void run() { for( int i = 0 ; i < 10 ; i++ ) { synchronized( toi ) { toi.bigWork("park"); } } } }
실행결과>> BW STEP 1 : Kim BW STEP 2 : Kim BW STEP 3 : Kim BW STEP 4 : Kim BW STEP 5 : Kim BW STEP 1 : Park BW STEP 2 : Park BW STEP 3 : Park BW STEP 4 : Park BW STEP 5 : Park
synchronized( toi ) {
....
}
{ ... } 의 영역에 들어가기 위해선 toi 가 가리키는 인스턴스가 가진 key 를 획득해야 한다. 모든 인스턴스는 하나의 키를 가지고 있다.
Kim 과 Park 이 toilet 에 거의 동시에 들어왔어도 Kim 이 먼저 열쇠를 갖고 들어가면 Park 은 열쇠가 반납될 때 까지 기다려야 한다.
열쇠를 가져야만 {
을 통과할 수 있고 , }
시점에서 열쇠는 인스턴스에게 반납된다.
synchronized
를 통해서 동시에 인스턴스를 이용하는 스레드에 의해서 벌어질 수 있는 상황 ( 하나의 객체를 여러 쓰레드가 동시에 이용하면 안되는 일 ) 을 막을 수 있다.