본문 바로가기
JAVA SE/이론 및 개념

[20.08.03/Day_18] Java SE / Thread 스레드 (multi thread, Daemon Thread 데몬 스레드, Thread Scheduling, Synchronized 동기화)

by 파프리카_ 2020. 8. 3.
728x90
반응형

[ Thread 스레드 ]

: 프로세스 내의 세부적 실행 단위(사전적 의미는 '실') 

⇒ 현재 실행 중인 프로그램의 세부적 실행단위
* process는 현재 실행중인 프로그램을 의미

ex)

예제 1.

동영상 재생 프로그램을 실행(process)한다.
영상서비스, 음향서비스, 자막서비스가 thread 로 동작된다.

 

예제 2. 클라이언트 프로그램

채팅 클라이언트 프로그램을 실행(process)한다.

→ 채팅 클라이언트 프로세스 내에 필수적인 실행단위는 두 가지이다.

→ 동시에 서비스되어야 하므로 멀티 스레드가 필요하다.

1. 친구들의 메세지를 입력받는 스레드

2. 친구들에게 메세지를 출력하는 스레드

 

예제 3. 서버 프로그램

채팅 서버 프로그램을 실행(process)한다.

→ 접속한 클라이언트 모두 서비스를 해야한다 : 서비스 내용은 동일

→ 접속한 클라이언트 수만큼 스레드를 각각 만들어 서비스해야 한다.

1. 서비스하는 스레드 수는 다수 (mulit thread)

2. 서비스를 표현하는 클래스는 하나(implement Runnable or extends Thread) (one service)

ex) 실생활 예

BC카드 콜센터에 여러 고객이 동시에 상담한다 → 콜센터 process

콜센터 직원 thread가 고객 당 생성되어 동시에 상담 서비스를 상담한다 → 콜센터 직원 thread(multi thread)

 

 


Thread 생성 방법

1) extends Thread

2) implements Runnable

* 자바는 단일 상속이므로, 다중 구현(다양한 계층구조 형성)이 가능한 방법인, 2번 방법을 더 선호한다.

 

//Thread 작업 내용을 정의하는 클래스

public class InputWorker implements Runnable{

	//Runnable interface의 abstract method를 구현한다
        @Override
	public void run(){
        //스레드 실행 내용
    }
}

 


Thread 실행 및 흐름 제어

  • start() : thread를 실행 가능 상태로 보낸다. ( jvm이 실행하는 것이지, 개발자가 실행하는 것은 X !! )
     jvm이 이후 스케쥴링을하여 선택될 경우 실행상태로 전이된다.
  • run() : thread의 실행 내용을 정의하는 영역
    jvm에 의해 스케쥴링을 받으면 run()이 실행되고 run()메서드 실행이 마치면 Thread는 종료된다.

//위 Thread를 이용해 Thread를 생성하는 코드

InputWorker worker = new InputWorker();
Thread thread = new Thread(worker);
thread.start(); //해당 Thread를 실행가능 상태로 보내고, jvm이 스케쥴링하면 run()실행

 

단일 스레드 예제

package step1;

//단일 thread 작동 
class Worker{
	public void work() {
		for (int i = 0; i < 5; i++) {
			System.out.println(i+1 +"번째 "+"Worker Class의 work method 실행");
		}
	}
}
public class TestThread1 {
	public static void main(String[] args) {
		System.out.println("**main thread 시작**");
		Worker w = new Worker();
		w.work();
		System.out.println("**main thread 시작**");
		
		/*	출력값 : 
		 * **main thread 시작**	
		 * 1번째 Worker Class의 work method 실행
		 * 2번째 Worker Class의 work method 실행
		 * 3번째 Worker Class의 work method 실행
		 * 4번째 Worker Class의 work method 실행
		 * 5번째 Worker Class의 work method 실행
	 	 * **main thread 시작**
	 	 */
	}
}

* step1 단일스레드 예제와 실행결과를 비교해 본다  

Multi Thread 멀티 스레드 예제 1

: extends Thread로 생성

: main 와 WorkerThread 즉 두개의  스레드가 동작되는 예제  

package step2;

// multiple thread
class Worker extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println(i+1 +"번째 "+"Worker Class의 run method 실행");
		}
	}
}

public class TestThread2 {
	public static void main(String[] args) {
		System.out.println("***main thread 시작***");
		/* Worker Thread 생성 및 실행 
		 * run() 메서드 실행이 아닌, 
		 * start()라는 Thread 클래스 메서드를 실행해야 한다
		*/
		Worker w = new Worker();
		w.start();
		System.out.println("***main thread 끝***");
		
		/*	출력값 : 
		 * ***main thread 시작***	
		 * ***main thread 끝***	
		 * 1번째 Worker Class의 run method 실행
		 * 2번째 Worker Class의 run method 실행
		 * 3번째 Worker Class의 run method 실행
		 * 4번째 Worker Class의 run method 실행
		 * 5번째 Worker Class의 run method 실행
	 	 */
	}
}

1. main thread 먼저 동작

2. w.start() → thread를 실행시키는 것이 아니라, 실행 가능하게끔 해줌

3. extends된 Worker 클래스 동작


Multi Thread 멀티 스레드 예제 2 (채팅 클라이언트)

: implements Runnable 로 생성

: 채팅 클라이언트 프로그램을 연상하며 두 개의 스레드를 생성시켜 실행해본다.

1. InputWorker Thread : 친구들의 메세지를 입력받는 일을 하는 thread

2. main Thread  - outputMessage() : 친구들에게 메세지를 출력하는 일을 하는 thread

package step3;

class InputWorker implements Runnable{
	//jvm에 의해 run이 가장 먼저 자동 호출됨.
	@Override
	public void run() {
		inputMessage();
	}
	
	public void inputMessage() {
		for (int i = 0; i < 10; i++) {
			System.out.println("친구들의 메세지를 입력받는 Input Thread");
		}
	}
}
public class TestThread3 {
	public void outputMessage() {
		for (int i = 0; i < 5; i++) {
			System.out.println("친구들의 메세지를 출력하는 Output Thread");
		}
	}
	public static void main(String[] args) {
		System.out.println("***main thread 시작***");
		//implement Runnable한 클래스를 이용해
		//thread를 생성하고 start()
		InputWorker w = new InputWorker();
		Thread thread = new Thread(w);
		thread.start();
		new TestThread3().outputMessage();
		System.out.println("***main thread 종료***");
	}
	/* 출력값:
	 * ***main thread 시작*** 
	 * 친구들의 메세지를 출력하는 Output Thread 
	 * 친구들의 메세지를 출력하는 Output Thread 
	 * 친구들의 메세지를 출력하는 Output Thread 
	 * 친구들의 메세지를 출력하는 Output Thread 
	 * 친구들의 메세지를 출력하는 Output Thread 
	 * *** main thread 종료*** 
	 * 친구들의 메세지를 입력받는 Input Thread 
	 * 친구들의 메세지를 입력받는 Input Thread 
	 * 친구들의 메세지를 입력받는 Input Thread
	 * 친구들의 메세지를 입력받는 Input Thread
	 * 친구들의 메세지를 입력받는 Input Thread 
	 * 친구들의 메세지를 입력받는 Input Thread 
	 * 친구들의 메세지를 입력받는 Input Thread 
	 * 친구들의 메세지를 입력받는 Input Thread 
	 * 친구들의 메세지를 입력받는 Input Thread 
	 * 친구들의 메세지를 입력받는 Input Thread
	 */
}

< InputWorker Class > 

1. InputWorker Thread의 작업 내용을 정의 ← class InputWorker implements Runnable {}

2. JVM이 스케쥴링하면 이 메서드가 자동호출된다. ← @Override  public void run() { }

 

< TestThread3 Class >  - main class

3. implement Runnable한 클래스를 이용해  thread를 생성

←  InputWorker w = new InputWorker();

Thread thread = new Thread(w);

4. 해당 Thread의 시작을 함

← thread.start();

5.  친구들의 메세지를 받는 메서드 형성

public void outputMessage(){}

6. outputMessage 실행  : main thread 종료 전에 반드시 수행됨 -- main thread 영역에 있기때문

new TestThread3().outputMessage()


Multi Thread 멀티 스레드 예제 3 (동영상)

: implements Runnable 로 생성

: 동영상 플레이어 프로세스로 가정 

1. VideoWorker 영상 작업 스레드

2. AudioWorker 음향 작업 스레드

package step4;

class VideoWorker implements Runnable {
	@Override
	public void run() {
		try {
			work();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void work() throws InterruptedException {
		for (int i = 0; i < 5; i++) {
			System.out.println("영상 작업 Thread(VideoWorker): " + i);
			Thread.sleep(1000); // 1초간 스레드 작업 일시 중단 후 재개
		}
	}
}

class AudioWorker implements Runnable {
	@Override
	public void run() {
		try {
			work();
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
	public void work() throws InterruptedException {
		for (int i = 0; i < 5; i++) {
			System.out.println("음향 작업 Thread(AudioWorker): "+ i);
			Thread.sleep(1000);
		}
	}
}

public class TestThread4 {
	public static void main(String[] args) {
		System.out.println("***main thread 시작***");
		new Thread(new VideoWorker()).start();
		new Thread(new AudioWorker()).start();
		System.out.println("***main thread 종료***");
		
		/* 출력값:
		 * ***main thread 시작*** 
		 * 영상 작업 Thread(VideoWorker): 0 
		 * *** main thread 종료*** 
		 * 음향 작업 Thread(AudioWorker): 0
		 * 영상 작업 Thread(VideoWorker): 1 
		 * 음향 작업 Thread(AudioWorker): 1 
		 * 음향 작업 Thread(AudioWorker): 2 
		 * 영상 작업 Thread(VideoWorker): 2
		 * 음향 작업 Thread(AudioWorker): 3 
		 * 영상 작업 Thread(VideoWorker): 3 
		 * 음향 작업 Thread(AudioWorker): 4 
		 * 영상 작업 Thread(VideoWorker): 4
		 */
	}
}

메인 스레드와는 무관하게 동작하며, 

VideoWorker thread와 AudioWorker thread는 동시작업된다.

 

 


Web Server 에서의 thread

출처 : https://m.blog.naver.com/PostView.nhn?blogId=ndb796&logNo=221205676326&proxyReferer=https:%2F%2Fwww.google.com%2F

 

> 다수의 client를 상대하기 위해서는 각자의 다양한 thread가 필요하다.

 

 

Multi Thread 멀티 스레드 예제 4 (채팅 서버)

: 하나의 클래스를 이용해 다수의 스레드를 생성해서 실행하는 예제

: 채팅 서버를 가정하고 서비스 내용은 동일하나, 접속한 다수의 클라이언트에게 동시에 서비스하기 위해

다수의 스레드를 생성하여 실행하도록 구현해본다.

package step5;

//채팅 서비스를 제공하는 클래스
class ServerWorker implements Runnable{
	@Override
	public void run() {
		try {
		work();
		}
		
		catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void work() throws InterruptedException {
		//현재 실행중인 스레드의 이름을 반환받는다.
		String name = Thread.currentThread().getName();
		for (int i = 0; i < 5; i++) {
			System.out.println(name + " 고객님에게 채팅 서비스 제공 Thread: "+ i);
			Thread.sleep(1000);
		}
	}
}

// 실행 class
public class TestThread5 {
	public static void main(String[] args) {
		//Thread 2개 동작 중
		//1. Main Thread
		//2. ServerWorker Thread
		System.out.println("***Main Thread 시작***");
		ServerWorker worker = new ServerWorker();
		
		//ServerWorker Thread 1
		Thread t1 = new Thread(worker, "앨리사");
		t1.start();
		
		//ServerWorket Thread 2
		Thread t2 = new Thread(worker, "윌");
		t2.start();
		
		//ServerWorket Thread 3
		Thread t3 = new Thread(worker, "일레븐");
		t3.start();
		
		System.out.println("***Main Thread 종료***");
		
		/* 출력값:
		 * ***Main Thread 시작*** 
		 * *** Main Thread 종료*** 
		 * 윌 고객님에게 채팅 서비스 제공 Thread: 0
		 * 앨리사 고객님에게 채팅 서비스 제공 Thread: 0
		 * 일레븐 고객님에게 채팅 서비스 제공 Thread: 0
		 * 앨리사 고객님에게 채팅 서비스 제공 Thread: 1
		 * 일레븐 고객님에게 채팅 서비스 제공 Thread: 1
		 * 윌 고객님에게 채팅 서비스 제공 Thread: 1
		 * 윌 고객님에게 채팅 서비스 제공 Thread: 2
		 * 일레븐 고객님에게 채팅 서비스 제공 Thread: 2
		 * 앨리사 고객님에게 채팅 서비스 제공 Thread: 2
		 * 일레븐 고객님에게 채팅 서비스 제공 Thread: 3
		 * 앨리사 고객님에게 채팅 서비스 제공 Thread: 3
		 * 윌 고객님에게 채팅 서비스 제공 Thread: 3
		 * 앨리사 고객님에게 채팅 서비스 제공 Thread: 4
		 * 일레븐 고객님에게 채팅 서비스 제공 Thread: 4
		 * 윌 고객님에게 채팅 서비스 제공 Thread: 4
		 */
	}
	
}

 

1. Main Thread와 ServerWorker Thread 3개가 동시에 동작하고 있다.

 

2. ServerWorker Class에서 현재 실행중인 스레드의 이름을 반환받는다.

←String name = Thread.currentThread().getName();

 

3. 서비스의 내용은 동일하므로, ServerWorker 하나로 다수의 Thread를 생성한다.

* 스레드 생성 시 스레드의 name을 함께 할당한다. 

← Thread t1 = new Thread(worker, "앨리사");

← Thread t2 = new Thread(worker, "윌");

← Thread t3 = new Thread(worker, "일레븐");

 

4. start 하여 동작시킨다.

← t1.start();

← t2.start();

← t3.start();


Thread Scheduling  

Thread State Diagram (출처 : https://cafe.naver.com/kostakosta203?iframe_url=/MyCafeIntro.nhn%3Fclubid=30164756 )

 

: 자바 스레드 스케쥴링은 우선순위 방식을 채택한다.

thread priority(스레드 우선순위)는 가장 낮은 1부터 가장 높은 10까지 이며,

우선순위를 따로 지정하지 않았을 경우 기본 우선순위인 5가 적용된다.

우선순위에 따라 스케쥴링을 받을 가능성이 높아진다.

 

> CPU가 system에서 동시 수행할 수 있는 thread 수는 제한되어 있다.

이 개수를 넘어서면 우선순위 방식이 적용된다.  우선순위가 높은 스레드가 스케쥴링의 우선권를 가진다.

 

* 우선순위 상수값

Thread.NORM_PRIORITY : 5  ← 기본 우선 순위

Thread.MIN_PRIORITY : 1 ← 가장 낮은 우선 순위

Thread.MAX_PRIORITY : 10  ← 가장 높은 우선 순위

 

 

java thread scheduling 예제

: test Thread를 20개 생성하여, start() 한 후, 그 중 하나의 우선순위를 가장 높게 설정한 다음 결과 확인

package step6;

class Worker implements Runnable{
	@Override
	public void run() {
		// 현재 진행 중인 Thread의 이름을 반환받음
		String name = Thread.currentThread().getName();
		int priority = Thread.currentThread().getPriority();
		for (long l = 0; l < 9000000000L; l++) {}
		System.out.println(name+" Thread 실행 완료! 우선 순위:"+priority);
	}
}
public class TestThread6 {
	public static void main(String[] args) {
		System.out.println("**Main Thread 시작**");
		
		Worker worker = new Worker();
		/*
		 * Thread t1 = new Thread(worker, "1번째 일꾼 Thread"); 
		 * t1.setPriority(9);
		 * t1.start();
		 */
		
		// 20개의 Thread를 생성하고 start하되, 9번째 Thread의 우선순위는 10으로 할당
		for (int i = 1; i < 21; i++) {
			Thread t = new Thread(worker, i+"번째 일꾼 Thread");
			if (i == 9)
				t.setPriority(Thread.MAX_PRIORITY);
			System.out.println(i+"번째 일꾼 Thread start!");
			t.start();
		}
		
		System.out.println("**Main Thread 종료**");
	}
}

 

[결과]

9번째 Thread 일꾼이 가장 먼저 실행된 것을 확인할 수 있다 ! 


Daemon Thread

: 백그라운드에서 실행되는 Thread. (aka.background Thread)

: 자신이 실행시킨 Thread가 종료되면 함께 종료된다.

ex) 워드 작업 프로그램 실행 시, 백그라운드에서 주기적으로 백업을 하도록 구현하고 이 백업스레드를 setDaemon(true)로 처리하면, 워드 프로그램 종료 시 함께 종료된다.

 

Daemon Thread 예제

package step7;

//Daemon Thread 예제
/* Thread class 2개 정의
 * 1. Word
 * 2. BackupWorker
 * 
 * Word Thread 시작 시, BackupWorker Thread가 시작하도록 코드는 구성되어있다.
 * Word Thread가 작업종료 되어, 해당 Thread가 종료되면 함께 BackupWorker Thread가 종료되도록
 * setDaemon(true)를 설정한다.
 */

class BackupWorker implements Runnable {
	@Override
	public void run() {
		while (true) {
			try {
				backup();
				Thread.sleep(3000); // 3초마다 백업처리
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public void backup() {
		System.out.println("문서 백업 처리");
	}
}

// Word Thread가 실핼될 때, 사장 먼저 BackupWorker Thread를 생성하여 start 시킨다.
class Word implements Runnable {
	@Override
	public void run() {
		BackupWorker b = new BackupWorker();
		Thread backupThread = new Thread(b);
		// Word Thread가 종료되면, BackupThread도 함께 종료되도록 설정한다.:setDaemon(true)
		backupThread.setDaemon(true);
		backupThread.start();

		for (int i = 0; i < 10; i++) {
			System.out.println("문서 작업 " + i);
			try {
				Thread.sleep(1000); // 1초마다 문서 작업
			} catch (InterruptedException e) {
				e.printStackTrace();
			} // catch
		} // for

		System.out.println("**문서 작업 종료**");
	}// run

}// Word class

public class TestThread7 {
	public static void main(String[] args) {
		System.out.println("**Main Thread 시작**");

		// 워드 작업 스레드 생성 start
		new Thread(new Word()).start();

		System.out.println("**Main Thread 종료**");
	}
}

1. Word Thread가 실핼될 때, 사장 먼저 BackupWorker Thread를 생성하여  start 시킨다.

← Thread backupThread = new Thread(new BackupWorker());

← backupThread.start();

2. Word Thread가 작업종료 되어, 해당 Thread가 종료되면

    함께 BackupWorker Thread가 종료되도록  setDaemon(true)를 설정한다.

← backupThread.setDaemon(true);

 

[ 결과 ]

 


[ Synchronized 동기화 ]

: 공유 자원에 대한 Thread의 동시 접근을 막기 위하여 순차적으로 처리하도록 명시하는 것

출처 : https://cafe.naver.com/kostakosta203?iframe_url=/MyCafeIntro.nhn%3Fclubid=30164756

 

  • java의 Thread는 multi threading 시,  multi-Thread를 통해 데이터를 공유할 수 있는 장점이 있다. (공유 자원)
    → 이를 이용해, 다수의 Thread를 생성하고,
    실행 시 한번 만든 Thread 객체의 자원을 Heap memory 영역에 하나로 저장되어,  공유할 수 있다.
    ( 서버의 메모리를 효율적으로 사용한다 )
  •  하지만 이 장점이 문제가 될 수 있다. 이럴 경우 synchronized 처리를 통해 해결할 수 있다.

  •  synchronized (동기화) 처리란 ?
    공유자원 영역을 순차적 처리를 하도록 명시하는 것을 의미한다.
    * 순차적 처리란 그 영역을 단일 스레드 환경으로 실행하는 것

    → 멀티 스레딩 시 공유자원의 안정성을 위해 사용한다.
    → 여러 스레드는 데이터를 공유해 사용할 수 있고, 이런 면은 스레드 방식의 큰 장점이지만 주의해야 될 사항이 있다. 데이터 자원의 데이터 조작 시 발생할 수 있는 문제에 대비하기 위해 데이터 조작영역을 단일 스레드 환경으로 만드는 synchronized가 필요하다.

[ synchronized의 주요 method ] 

  • wait() : 특정 조건이 될 경우 현재 실행되는 스레드를 wait pool에 대기시킬 때 사용하는 메서드
  • notify() or notifyAll() : 특정 조건이 될 경우 스레드를 wait pool에서 벗어나 object lock pool 상태로 보내는 메서드
    *참고 : object lock pool이란 synchronized 처리된 영역에 다른 스레드가 실행 중인 경우 대기하는 pool

 

+ java se API 중 동기화처리(thread-safe)에 대한 정리

 

[ Collection 계열 자료구조]

동기화처리(thread-safe)가 되어있는 대표적인 자료 구조

→ Vector, Hashtable 

동기화처리가 되어있지 않은 자료구조

→ ArrayList, HashMap

 

하지만 최근에는 성능상의 이유로 Vector, Hashtable 는 거의 이용하지 않으며,

java.util.Collections의 static method의 synchronizedList() or synchronizedMap()을 이용해

thread-safe(동기화처리)한 컬렉션을 필요시 생성해 사용한다.

 

[ 문자열 ]

  • String :  문자열 변경 시, 문자열 자체는 변하지 않는다
    - literal pool(문자열 상수영역)에 저장하고 공유해서 사용 (자원을 아끼기 위해서)
    - 문자열을 수정하면, 새로운 문자열을 만든다.

  • String Builder : 문자열 변경 시,  문자열 자체가 변경된다.
    - 문자열을 자주 수정할 때, 사용한다.
    - 동기화를 처리가 되어있지 않다.

  • String Buffer  : String Builder와 마찬가지로, 문자열 변경 시 문자열 자체가 변경된다.
    - 동기화처리가 되어 있다. (thread - safe)

 

multi-Thread를 통한 데이터 공유 예시

→ 자바 멀티 스레드는 데이터를 공유할 수 있는 장점을 가진다

class Worker implements Runnable {
	private String info = "공유자원";

	@Override
	public void run() {
		String name = Thread.currentThread().getName();

		for (int i = 0; i < 3; i++) {
			System.out.println(name + " " + info + " 사용하여 작업수행");
			
			try {
				Thread.sleep(500); // 0.5초 단위로 thread 실행
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class TestThread9 {
	public static void main(String[] args) {
		System.out.println("***Main Thread 시작***");
        
		Worker w = new Worker(); // Thread Object인 w가 공유자원으로 사용되고 있다
		Thread t1 = new Thread(w, "첫번째 Thread");
		Thread t2 = new Thread(w, "두번째 Thread");
		Thread t3 = new Thread(w, "세번째 Thread");
        
                t1.start();
                t2.start();
                t3.start();
        
		System.out.println("***Main Thread 종료***");
		
		/* 출력값:
		 * ***Main Thread 시작***
		 * ***Main Thread 종료*** 
		 * 첫번째 Thread 공유자원 사용하여 작업수행 
		 * 두번째 Thread 공유자원 사용하여 작업수행 
		 * 세번째 Thread 공유자원 사용하여 작업수행 
		 * 두번째 Thread 공유자원 사용하여 작업수행 
		 * 세번째 Thread 공유자원 사용하여 작업수행 
		 * 첫번째 Thread 공유자원 사용하여 작업수행 
		 * 첫번째 Thread 공유자원 사용하여 작업수행 
		 * 두번째 Thread 공유자원 사용하여 작업수행 
		 * 세번째 Thread 공유자원 사용하여 작업수행
		 */
	}
}

 

 > 공유자원을 사용할 때,  삽입(insert) / 삭제(delete) 시에는 유의해서 사용해야 한다.

Thread 별로 Stack에서 각각의 메모리로 생성된다.

(main Thread, Thread t1, Thread t2, Thread t3 다 다른 Stack memory에 존재)

이 때, Synchronized를 통해 동시 접근(공유)를 막을 수 있다.

 

생성 방법

  1. synchronized 함수를 만들어 사용한다
  2. synchronized block을 사용한다 → 예제에서는 이 방법을 사용했다

Synchronized 필요성을 확인하는 예제 (문제발생)

→ 하나의 자원에 다수의 Thread가 실행되어 자원을 조작하는 경우, 발생할 수 있는 문제점을 확인하는 예제

class Toilet implements Runnable{
	// instance 변수 : 각 Thread의 공유자원
	private boolean seat; //instance 변수의 기본초기화 : false 할당
	
	@Override
	public void run() {
		try {
		//use 메서드는 String user가 매개변수로 들어와야 하기 때문에,
		//현재 thread의 이름을 user값으로 넣어준다.
		use(Thread.currentThread().getName());
		
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void use(String user) throws InterruptedException {
		if (seat == false) { //seat이 비어있으면 사용가능
			Thread.sleep(1000);
			seat = true; // 들어가기 전, seat을 true로 채워준다. (사용불가능)
			System.out.println(user + " 입장");
			System.out.println(user + " 사용");
			Thread.sleep(3000);
			System.out.println(user + " 퇴장");
			seat = false; //퇴장 후, seat을 다시 비워줌 (사용가능)
		} else { //seat가 true이면 화장실을 사용하는 상태이므로 입장불가
			System.out.println(user + "님이 화장실 사용중이므로 입장불가");
		}
	}
}
public class TestSynchronizedEx {
	public static void main(String[] args) {
		Toilet toilet = new Toilet();
		Thread t1 = new Thread(toilet, "멜리사 Thread");
		Thread t2 = new Thread(toilet, "크리스타 Thread");
		t1.start();
		t2.start();
	}
}

 

(수정 전) multi-threading 시, 데이터를 공유할 때 일어날 수 있는 문제

 

 

Synchronized 를 이용하여 문제를 해결한 예제 (해결)

→ 해결방안 : 공유자원 ( Toilet의 seat ) 에 접근한 영역을 synchronized 처리하면 된다. 

class Toilet implements Runnable {
	// instance 변수 : 각 Thread의 공유자원
	private boolean seat; // instance 변수의 기본초기화 : false 할당

	@Override
	public void run() {
		try {
			// use 메서드는 String user가 매개변수로 들어와야 하기 때문에,
			// 현재 thread의 이름을 user값으로 넣어준다.
			use(Thread.currentThread().getName());

		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	// !! use() method를 synchronized 처리해준다 !!
	public synchronized void use(String user) throws InterruptedException {
		
		System.out.println(user + " 입장");
		System.out.println(user + " 사용");
		System.out.println(user + " 퇴장");

	}
}

public class TestSynchronizedSolution {
	public static void main(String[] args) {
		Toilet toilet = new Toilet();
		Thread t1 = new Thread(toilet, "멜리사 Thread");
		Thread t2 = new Thread(toilet, "크리스타 Thread");
		t1.start();
		t2.start();
	}
}

 

(수정 후) 단일 Thread화되어 Thread가 순차적으로 실행이 된다.


자바의 Thread 클래스의 주요 메소드


  - run(): 쓰레드의 실질적인 코드 블록입니다.
  - setDaemon(): 메인 Thread가 종료되었을 때 자동으로 종료되도록 설정합니다.
  - start(): 해당 Thread의 시작을 알리는 함수입니다.
  - sleep(long): 주어진 밀리 초(Milli-Second) 동안 쓰레드 작동을 일시정지 합니다. (sleep(1000) = 1초 작업중단)
  - interrupt(): Thread를 강제로 종료시킵니다.
  - isAlive(): Thread가 살아있는지 물어봅니다.
  - join(): 이 Thread가 종료될 때까지 기다린 후에 실행합니다.
  - yield(): 실행을 중단하고, 다른 Thread에게 CPU 자원을 양보합니다.

 

 

 

 

 

 

728x90
반응형