자바(Java)의 기초 박살내기 - 스레드(Thread)


이번 시간에는 스레드에 대해서 공부하겠습니다. 스레드란 어떠한 프로그램내에서 특히 프로세스 내에서 실행되는 흐름의 단위입니다. 


 1. Thread 


- 메모리를 할당받아 실행 중인 프로그램을 프로세스라고 합니다.


- 프로세스 내의 명령어 블록으로 시작점과 종료점을 가진다.


- 실행중에 멈출 수 있으며 동시에 수행 가능하다.


어떠한 프로그램내에서 특히 프로세스 내에서 실행되는 흐름의 단위.



 2. Thread 클래스 


- JDK에서 지원하는 java.lang.Thread 제공


<Thread 생성자>

 Thread()

 

 Thread(String s)

 스레드 이름

 Thread(Runnable r)

 인터페이스 객체 

 Thread(Runnable r, String s)

 인터페이스 객체와 스레드 이름 


<Thread 메소드>

 static void sleep(long msec) 

 throws Interrupted Exception

  msec에 지정된 밀리초 동안 대기

 String getName()

 스레드의 이름을 s로 설정 

 void setName(String s)

 스레드의 이름을 s로 설정

 void start()

 스레드를 시작 run() 메소드 호출

 int getPriority()

 스레드의 우선 순위를 반환 

 void setpriority(int p)

 스레드의 우선순위를 p값으로 

 boolean isAlive()

 스레드가 시작되었고 아직 끝나지 않았으면 true 끝났으면 false 반환 

 void join() throws InterruptedException

 스레드가 끝날 때 까지 대기  

 void run()

 스레드가 실행할 부분 기술 (오버라이딩 사용)

 void suspend()

 스레드가 일시정지 resume()에 의해 다시시작 할 수 있다. 

 void resume()

 일시 정지된 스레드를 다시 시작. 

 void yield()

 다른 스레드에게 실행 상태를 양보하고 자신은 준비 상태로 



 2. Thread 생성 


- 2가지 방법

① 직접 상속 받아 스레드 생성

② Runnable 인터페이스를 구현해서 생성


- Thread 클래스 이용

- Thread 클래스로 부터 제공되는 run()메소드 오버라이딩해서 사용

- Ex)

class ThreadA extends Thread

{

public void run()

{

// 수행할 문장들 기술

}

}

- 실제 사용

- ThreadA TA = new ThreadA();

  TA.start();


<예제 소스코드1 - ThreadTest.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ThreadTest extends Thread
{
    public void run()
    {
        // 인터럽트 됬을때 예외처리
        try
        {
            for(int i = 0 ; i < 10 ; i++)
            {
                // 스레드 0.5초동안 대기
                Thread.sleep(500);
                System.out.println("Thread : " + i);
            }
        }catch(InterruptedException e)
        {
            System.out.println(e);
        }
    }
}
 
cs

<예제 소스코드2 - Thread1.class>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Thread1 
{
    public static void main(String args[])
    {
        ThreadTest t1 = new ThreadTest();
        ThreadTest t2 = new ThreadTest();
        
        // 1. 동시에 똑같은 숫자가 나오고(start)
        /*t1.start();
        t2.start();*/
        
        // 2. 번갈아가면서 나옴(run)
        t1.run();
        t2.run();
    }
}
cs


<결과1 - .start()>


<결과2 - .run()>



- 현재의 클래스가 이미 다른 클래스로부터 상속 받고 있다면 Runnable 인터페이스를 이용하여 스레드를 생성할 수 있습니다.


- Runnable 인터페이스는 JDK 라이브러리 인터페이스이고 run()메소드만 정의되어 있다.


<예제 소스코드1 - RunnableTest.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Runnable 인터페이스 상속
public class RunnableTest implements Runnable
{
    // Runnable 인터페이스의 run()오버라이딩
    public void run()
    {
        try    // 인터럽트 예외처리
        {
            for (int i=0 ; i<10 ; i++
            {
                // 대기시간 0.2초
                Thread.sleep(200);
                System.out.println("스레드 :" + i);
            }
            
        }catch(InterruptedException e ) 
        {
            e.printStackTrace();
        }
 
    }
}
cs


<예제 소스코드2 - Thread2.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Thread2 
{
    public static void main(String args[])
    {
        // Runnable 인터페이스 객체생성
        RunnableTest Obj1 = new RunnableTest();
        RunnableTest Obj2 = new RunnableTest();
        
        // Runnable 객체를 매개변수로 하여 스레드 객체 th생성
        Thread th1 = new Thread(Obj1);
        Thread th2 = new Thread(Obj2);
        
        th1.start();
        th2.start();
    }
}
cs


<결과>



- run()메소드가 종료하면 스레드는 종료된다.

- 스레드를 계속 실행시킬려면 run()메소드를 무한루프 속에 실행되어야합니다.


- 한번 종료한 스레드는 다시 시작시킬수 없습니다.

- 스레드 객체를 다시 생성해야 합니다.


- 한 스레드에서 다른 스레드를 강제 종료할 수 있습니다.


- 스레드의 상태 6가지

① NEW : 스레드가 생성되었지만 스레드가 아직 실행할 준비가 되지 않았음

② RUNNABLE : 스레드가 실행되고 있거나 실행준비되어 스케쥴링은 기달리는 상태

③ WAITING : 다른 스레드가 notify(), notifyAll()을 불러주기 기다리고 있는 상태(동기화)

④ TIMED_WAITING : 스레드가 sleep(n) 호출로 인해 n 밀리초동안 잠을 자고 있는 상태

⑤ BLOCK : 스레드가 I/O 작업을 요청하면 자동으로 스레드를 BLOCK 상태로 만든다.

⑥ TERMINATED : 스레드가 종료한 상태

- 스레드 상태는 JVM에 의해 기록 관리된다.



 3. Daemon Thread 


- 동일한 프로세스 안에서 다른 스레드의 수행을 돕는 스레드로 다른 스레드를 서비스 해주면서 다른 스레드가 모두 종료되면 자신도 종료되는 스레드


- 프로그램이 종료되는 것을 막지 않으며 가비지 컬렉터나 메인 스레드가 데몬 스레드입니다.


- 스레드를 생성하고 setDaemon(true)를 설정하면 됨


- 스레드가 시작하기 전에 설정해야 합니다. 


<예제 소스코드 - Thread3.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Thread3 extends Thread
{
    public void run()
    {
        try
        {
            System.out.println("Daemon Thread Start!");
            sleep(10000);
            System.out.println("Daemon Thread End!");
        }catch(Exception e){ }
    }
    
    public static void main(String args[])
    {
        Thread3 t = new Thread3();
        // Thread가 종료되지 않기 때문에 끝나질 않음
        t.setDaemon(true);     
        t.start();
        System.out.println("Main Method End!");
    }
}
cs


<결과>




 4. 우선 순위(Priority) 


- 2개 이상의 스레드가 동작 중일 때 우선 순위를 부여하여 우선 순위가 높은 스레드에게 실행의 우선권을 부여할 수 있습니다.


- 우선 순위를 지정하기 위한 상수를 제공

- static final int MAX_PRIORITY : 우선순위 10 - 가장 높은 우선 순위

static final int MIN_PRIORITY  :  우선순위 1 - 가장 낮은 우선 순위

static final int NORM_PRIORITY : 우선순위 5 - 보통의 우선 순위


- 스레드 우선 순위는 변경 가능

- void setPriority(int priority)

- int getPriority()


- main()스레드의 우선 순위 값은 초기값이 5


- JVM의 스케쥴링 규칙

- 철저한 우선 순위 기반

- 가장 높은 우선 순위의 스레드가 우선적으로 스케쥴링

- 동일한 우선 순위의 스레드는 돌아가면서 스케쥴링(라운드 로빈)


<예제 소스코드1 - PriorityTest.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class PriorityTest extends Thread
{
// 출력생성자
    PriorityTest(String str)
    {
        super(str);
    }
    
    public void run()
    {
        try
        {
            // 각각 1초 간격으로 스레드 
            for(int i = 0 ; i < 10 ; i++)
            {
                Thread.sleep(1000);
                System.out.println(getName() + i + "번째 수행");
            }
        }catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }
    
}

cs


<예제 소스코드2 - Thread4.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Thread4 
{
    public static void main(String args[])
    {
        PriorityTest t1 = new PriorityTest("우선 순위가 낮은 스레드");
        PriorityTest t2 = new PriorityTest("우선 순위가 높은 스레드");
        t1.setPriority(Thread.MIN_PRIORITY);    // 최소 우선 순위 지정
        t2.setPriority(Thread.MAX_PRIORITY);    // 최대 우선 순위 지정
           t1.start();
        t2.start();
    }
}
 
cs


<결과>



 5. 스레드 종료 


 - 스스로 종료

- run()메소드에 예외처리에 return을 넣어 스스로 종료


 - 타 스레드에서 강제 종료 : interrupt()메소드 호출하면 InterruptedException이 발생


<예제 소스코드1 - ThreadInterrupt.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ThreadInterrupt extends Thread
{
    // 출력 생성자
    ThreadInterrupt(String str)
    {
        super(str);
    }
    
    public void run()
    {
        try
        {
            for (int i = 0 ; i < 10 ; i++)
            {
                Thread.sleep(1000);
                System.out.println(getName() + i + "번쨰 수행");
            }
        }catch(InterruptedException e)
        {    // 인터럽트 걸릴시 
            System.out.println("스레드 강제 종료");
            return;
        }
    }
}
cs



<예제 소스코드2 - Thread5.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Thread5 
{
    public static void main(String args[])
    {
        ThreadInterrupt th = new ThreadInterrupt("스레드");
        th.start();
        
        // 3초에 인터럽트
        try
        {
            Thread.sleep(3000);
        }
        catch(InterruptedException e)
        {}
        th.interrupt();
    }
}
cs


<결과>



 6. 멀티 스레드 (Multi Thread) 


- 여러 개의 스레드가 동시에 수행되면서 공유할 수 있을때

- 공유되는 부분은 상호 배타적으로 사용되어야 합니다.

-Dead Lock 문제

- 멀티 스레드를 사용할 때 주의할 점중의 하나로 프로그램에서 스레드를 잘못 만들면 프로그램의 수행이 이루어 지지 않고 무한 수행하는 Dead Lock을 만들 수 있습니다.

- 임계 영역(critical section)

- 공유 자원을 사용하는 코드영역을 임계영역이라 합니다.

- 이 부분에서는 공유 자원을 동시에 수정할 수 없도록 상호 배타적으로 실행될 수 있도록 작성되어야 합니다.


- 자바에서 상호배제 문제를 해결하는 방법

- 자바는 한 순간에 하나의 스레드만 실행할 수 있는 synchronized method 제공

- 한 스레드가 synchronized method를 수행 중이면 다른 스레드는 대기합니다.


- 처리방법

- 공유 자원에 접근하는 메소드의 앞에 synchronized 메소드로 지정 합니다.

- 공유 자원을 사용하는 영역을 synchronized(객체명)의 블록으로 지정합니다.



<멀티 스레드 X 예제 소스코드1 - Account.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Account 
{
    int balance = 1000;
    public void withdraw(int money)
    {
        // 잔고가 찾으려는 금액보다 클 때
        if(balance >= money)
        {
            try
            {    // 1초 
                Thread.sleep(1000);
            }catch(Exception e) { }
                balance -= money;
        }
    }
}
cs


<멀티 스레드 X 예제 소스코드2 - Bank.class>

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
class Bank extends Thread 
{
    static Account obj = new Account();
    public Bank(){}
    public Bank(String name) { super(name); }
    public void run() 
    {
        // 무한 반복
        while(true
        {    
            // 찾는 금액은 랜덤
            int money = (int)(Math.random() * 3 + 1* 100;
            // 원금이 찾는 금액보다 클 때 
            if(obj.balance >= money)
            {
                System.out.println(getName() + " : 원본의  balance:" + obj.balance);
                System.out.println(getName() + " : 찾는 금액:" + money);
                obj.withdraw(money);
                System.out.println(getName() + " : 수정된 balance:" + obj.balance);
            }
            else
            {
                System.out.println("잔액 부족");
                break;
            }
        }
    } 
}
 
cs


<멀티 스레드 X 예제 소스코드3 - Thread6.class>

1
2
3
4
5
6
7
8
9
10
11
public class Thread6 
{
    public static void main(String args[])
    {
        Bank t1 = new Bank("ATM");
        Bank t2 = new Bank("은행");
        
        t1.start();
        t2.start();
    }
}
cs


<결과 - 돈을 찾았음에도불고하고 갱신이 바로바로 되지 않는다.>



<멀티 스레드 O 예제 소스코드1 - Account.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Account 
{
    int balance = 1000;
 
    public void withdraw(int money)
    {
        if(balance >= money)
        {
            try
            {
                Thread.sleep(1000);
            }catch(Exception e) { }
                balance -= money;
        }
    }
}
cs

<멀티 스레드 O 예제 소스코드2 - Bank.class>
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
public class Bank extends Thread
{
    static Account obj = new Account();
    public Bank() { }
    public Bank(String name) { super(name); }
    public void run()
    {
        while(true)
        {
            // 멀티 스레드 
            synchronized(obj)
            {
                int money = (int)(Math.random() * 3 + 1*100;
                if(obj.balance >= money)
                {
                    System.out.println(getName() + " : 원본의 balance : " + obj.balance);
                    System.out.println(getName() + ": 찾는 금액 : " + money);
                    obj.withdraw(money);
                    System.out.println(getName() + " : 수정된 balance : " + obj.balance);
                }else
                {
                    System.out.println("잔액 부족");
                    break;
                }
            }
        }
    }
}
cs

<멀티 스레드 O 예제 소스코드3 - Thread6.class>
1
2
3
4
5
6
7
8
9
10
11
public class Thread6 
{
    public static void main(String args[])
    {
        Bank t1 = new Bank("ATM");
        Bank t2 = new Bank("은행");
        
        t1.start();
        t2.start();
    }
}
cs


<결과 - 돈을 찾게 되면 바로바로 갱신이 된다.>



 7. 타이머 활용 


- public class Timer extends Object

- 백그라운드 Thread로 실행되는 일을 1회 또는 정기적으로 반복 실행되도록 스케쥴링 해주는 클래스


<생성자>

 Timer()

 새로운 타이머를 작성 

 Timer(boolean isDaemon)

 daemon으로 실행되는 타이머 

 Timer(String name)

 지정된 이름의 thread를 가지는 타이머 

 Timer(String name, boolean isDaemon)

 지정된 이름의 thread를 가지는 새로운 타이머 


<메소드>

 void cancel()

 현재 스케쥴 되고 있는 일을 파기해서 타이머를 종료 

 void schedule(TimerTask task, Date time)

 지정한 시간으로 지정한 일이 실행되도록 스케쥴 

 void schedule(TimerTask task, Date firstTime, long period)

 지정한 일이 지정한 시간에 개시되어 기간을 가지고 반복

 void schedule(TimerTask task, long delay)

 지정한 지연 후에 지정한 일이 실행되도록 스케쥴

 void schedule(TimerTask task, long delay, long period)

지정한 일이 지정한 지연 후에 개시되어 기간을 가지고 반복 


- public abstract class TimerTask extends Object implements Runnable

- Timer에 해 1회 또는 반복해 실행하도록 스케쥴 되는 일


<생성자>

 protected TimerTask()

 새로운 타이머 일이 작성


<메소드>

 boolean cancel()

 이 타이머 일을 취소 합니다.

 abstract void run()

 이 타이머 일에 실행되는 액션 입니다. 

 long scheduledExecutionTime()

 이 일이 최근 실제로 실행하도록 스케쥴 되었다라는 실행 시간을 돌려줍니다.


<타이머 예제 소스코드1 - Mytask.class>

1
2
3
4
5
6
7
8
9
10
import java.util.TimerTask;
 
public class MyTask extends TimerTask
{
    // "타이머 호출"
    public void run()
    {
        System.out.println("타이머 호출");
    }
}
cs


<타이머 예제 소스코드2 - TimerTest.class>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Timer;
 
public class TimerTest 
{
    public static void main(String args[])
    {
        MyTask task =new MyTask();
        // 타이머 객체 생성
        Timer timer = new Timer();
        
        // 1초후에 0.5초 간격으로 반복
        timer.schedule(task, 1000500);
        try
        {
            // 5초후에 스레드를 멈춘다.
            Thread.sleep(5000);
        }catch(InterruptedException exc) { timer.cancel(); }
        // 타이머 취소
        timer.cancel();
    }
}
 
cs



<결과>




  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유