티스토리 뷰

Chapter 12. 스레드

 

▶ 학습 목표

: 스레드가 무엇인지 설명할 수 있고 스레드를 생성하고 구현할 수 있다.

 

 

 

 

 

 

12-1. 스레드

: 하나의 프로세스 안에서 두가지 이상의 일을 하도록 도와준다.

 

 

 

- 스레드와 프로세서

 

프로그램을 실행하면 메모리가 할당되어 실행(=객체)되는데 이때 실행 중인 프로그램을 프로세스라고 한다.

프로세스에서 작업을 수행하는 것을 스레드라고 한다.

두가지 이상 작업을 할때 두개 이상의 스레드가 필요한다.

두개 이상의 작업을 하는 프로세스를 멀티스레드 프로세스라고 한다.

 

 

 

프로세스 실행되는 두가지 방식

 

1. 시간분할 방식

모든 프로세스에게 동일한 시간을 할당하고 골고루 실행(=라운드로빈)(=병렬)

 

 

2. 선점형 방식

각각의 프로세스에게 우선순위를 부여하고 우선순위가 높은 순으로 실행된다.

우선순위가 높은 프로세스가 잠시 멈추면 그다음 순위의 프로세스가 동작한다.(=직렬)

 

 

 

 

12-2. 스레드의 생성

: 스레드를 만든느 방법은 Thread클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있다.

Runnable 인터페이스를 사용하면 다중상속이 가능하다.

 

 

 

 

 

- Thread 클래스 상속받아서 만들기

 

Thread 클래스를 상속받고 run() 메서드를 오버라이딩한다.

class Th1 extends Thread
{
    public void run()
    {
    	//작업 내용
    }
}

 

실행할 때

Th1 t1 = new Th1();  //인스턴스 생성
t1.start(); //스레드 실행

 

 

 

 

 

- Runnalbe 인터페이스 구현

 

Runaable 인터페이스를 구현하도 추상 메서드인 run()을 오버라이딩한다.

 

class Th2 implements Runnalbe
{
    public void run()
    {
		//작업내용
    }
}

 

 

출력하고자 할 때

Th2 t2 = new Th2();  //인스턴스 생성
Thread t = new Thread(t2);  //생성한 객체를 생성하는 클래스의 파라미터로하여 스레드 생성
t1.start(); //스레드 실행

 

 

 

 

exam-93

Thread 클래스 상속과 Runaable 인터페이스 구현한 스레드를 각각 실행하는 클래스 예시

package chapter11;

	class MyThread extends Thread 
	{
		public void run() 
		{
			for(int i=0; i<10; i++) 
			{
				System.out.println("스레드 진행 중"+i);
			}
		}
	}
class MyThread2 implements Runnable
{
	public void run() 
	{
		for(int i=0; i<10; i++) 
		{
			System.out.println("스레드 진행 중"+i);
		}
	}
}
public class Thread1 
{

	public static void main(String[] args) 
	{
		MyThread th1 = new MyThread();
		Runnable th2 = new MyThread2();  //스레드 각각 생성 직렬
		
		Thread t = new Thread(th2);
        
		th1.start();
		t.start();  //스레드 병렬처리
		
		for(int i=0;i<10; i++) 
		{
			System.out.println("메인 함수 진행 중 : " +i);
		}

	}

}

 

 

 

 

 

 

 

 

12-4. 스레드의 라이프사이클

: 스레드는 네가지 상태로 분류할 수 있고 상태가 변하는 주기를 라이프 사이클이라고 한다.

 

 

 

스레드의 상태

 

1. new

스레드가 new 키워드를 통해 인스턴스화(객체 생성)된 상태이다.

Runnable이 될 수 있는 상태이고 아직 대기열에 올라가지 않은 상태이다.

 

 

2. Runnalbe

start() 메서드가 호출되면 new 상태에서 Runnable 상태가 된다.

Runnable 상태가 되면 실행할 수 있는 상태로 대기하게 된다.

스케줄러에 의해 선택이 되면 run() 메서드를 바로 수행한다.

 

 

3. Blocked

실행 중인 스레드가 sleep(), join() 메서드를 호출하게 되면 Blocked 상태가 된다.

Blocked 사태가 되면 스케줄러에 의해 선택 받을 수 없다.

(sleep, join 에 할당된 시간이 지나고나면 사용 가능)

 

 

4. Dead

run() 메서드의 실행을 모두 완료하게 되면 스레드는 Dead 상태가 된다.

할당 받은 메모리와 정보가 삭제된다.

 

 

 

 

- sleep()

 

: sleep() 메서드는 스레드를 지정된 시간 동안 block 상태로 만든다.

시간 단위는 1초를 1000초로 한다. 

지정된 시간이 지나면 다시 runnable 상태로 돌아간다.

 

 

 

 

exam-95

sleep()의 block효과를 이용해 카운트다운한다.

 

package chapter11;

class SleepThread extends Thread
{
	public void run() 
    {
		System.out.println("카운트다운 5초");
		for(int i=5; i>=0; i--) 
        {
			System.out.println(i);
			if(i!=0) 
            {
				try 
                {
					Thread.sleep(1000);  //시간지정1초동안 blcok상태
				}catch(InterruptedException ie) 
                {
					System.out.println(ie.toString());
				}
			}
		}
		System.out.println("종료");
	}
}

public class Sleep2 
{

	public static void main(String[] args) 
	{
		SleepThread t = new SleepThread();
		t.start();

	}

}

 

카운트다운 5초
5
4
3
2
1
0
종료

 

 

 

 

 

 

 

 

 

 

12-5. 스레드의 동기화

: 멀티 스레드로 작업을 할 때, 스레드 간의 작업이 간섭되지 않도록하는 것이다.

 

단일 스레드로 작업을 할 때는 프로세스의 자원을 사용하는데 문제가 없지만 멀티스레드 프로세스의 경우 여러 스레드가 같은 자원을 공유하기 때문에 문제가 생길 수 있다.

A,B가 같은 메서드를 사용할 때 A가 메서드를 사용 완료 후 결과를 반영하기 전에 B가 메서드를 사용하게 되면 B의 메서드 사용으로 A의 결과값에 영향을 받는다.

 

이러한 문제가 발생하지 않도록 하는 것이 동기화이다.

 

 

 

 

 

 

 

exam-98

티켓팅 관련 코드 예시

package chapter11;

class MyThreadB implements Runnable
{
	Ticketing ticket = new Ticketing();
	public void run()   //병렬 실행
	{
		ticket.ticketing();
	}
}

class Ticketing{
	int ticketNumber=1;
	public void ticketing() 
	{
		if(ticketNumber>0) 
		{
			System.out.println(Thread.currentThread().getName()+"가 티켓팅성공");
			ticketNumber=ticketNumber-1;
		}
		
		else 
		{
			System.out.println(Thread.currentThread().getName()+"가 티켓팅실패"); 
			//getname()클래스 이름 반환,currentThread()현재 실행중인 스레드 이름
		}
		System.out.println(Thread.currentThread().getName()+"가 티켓팅시도 후 티켓수 : "+ticketNumber);
	}
}

public class Synchronized1 
{

	public static void main(String[] args) 
	{
		MyThreadB s1 = new MyThreadB();
		Thread t1 = new Thread(s1,"A");  //싱글
		Thread t2 = new Thread(s1,"B");   //싱글
		Thread t3 = new Thread(s1,"C");   //싱글
		
		t1.start();  //run()실행 멀티프로세싱 병렬
		t2.start();  //병렬
		t3.start();   //병렬

	}

}

 

A가 티켓팅성공
C가 티켓팅성공
B가 티켓팅성공
A가 티켓팅시도 후 티켓수 : 0
C가 티켓팅시도 후 티켓수 : -1
B가 티켓팅시도 후 티켓수 : -2

 

위와 같은 코드를 실행하면 

병렬처리가 되어 if문의 값이 바뀌기 전에 통과할 수도 있다.(문제 발생)

 

이러한 코드를 동시화 시켜주고자 하면 public void ticketing()의 명령어에 synchronized 를 추가하면 문제를 해결할 수 있다.

 

public synchronized void ticketing() 으로 변경하면 문제가 해결된다.

자물쇠를 건다고 생각.

한 스레드가 먼저 작업하고 있을 떄 다른 스레드의 접근을 막는다.

 

 

 

 

 

synchronized 추가했을 때

 

package chapter11;

class MyThreadB implements Runnable
{
	Ticketing ticket = new Ticketing();
	public void run() 
	{
		ticket.ticketing();
	}
}

class Ticketing{
	int ticketNumber=1;
	public synchronized void ticketing() 
	{
		if(ticketNumber>0) 
		{
			System.out.println(Thread.currentThread().getName()+"가 티켓팅성공");
			ticketNumber=ticketNumber-1;
		}
		
		else 
		{
			System.out.println(Thread.currentThread().getName()+"가 티켓팅실패"); 
			//getname()클래스 이름 반환,currentThread()현재 실행중인 스레드 이름
		}
		System.out.println(Thread.currentThread().getName()+"가 티켓팅시도 후 티켓수 : "+ticketNumber);
	}
}

public class Synchronized1 
{

	public static void main(String[] args) 
	{
		MyThreadB s1 = new MyThreadB();
		Thread t1 = new Thread(s1,"A");
		Thread t2 = new Thread(s1,"B");
		Thread t3 = new Thread(s1,"C");
		
		t1.start();
		t2.start();
		t3.start();

	}

}
A가 티켓팅성공
A가 티켓팅시도 후 티켓수 : 0
B가 티켓팅실패
B가 티켓팅시도 후 티켓수 : 0
C가 티켓팅실패
C가 티켓팅시도 후 티켓수 : 0

 

 

위의 코드는 public synchronized void ticketing() 를 추가하여 변경한 코드이다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday