수업/└Java

[CH12]Thread

onlyun 2022. 2. 7. 09:06

 

 

1. Thread(스레드) 기본

하드디스크 Process cpu
프로그램을 설치하는 곳 설치한 프로그램을 사용하기 위해
메모리에 로딩
프로그램 실행

-멀티 프로세스 : 한글파일을 2개 띄우는 것(로딩하는 것)

-스레드(Thread) : 하나의 프로세스 안에 여러 경로로 실행하는 것. cpu를 사용하는 최소단위.

                        프로세스 내에서 실행되는 흐름 단위

 하나의 프로세스 안에 하나의 스레드가 있을 수도 있고 여러 개 있을 수도 있음. - 멀티 스레드

 

(1) 멀티 스레드 필요성

동시 출력 가능 ex) 영상과 자막이 동시에 출력되어야 할 때, 영상 스레드와 자막 스레드.

ex) 채팅을 동시에 진행할 때, 송식 스레드와 수신 스레드

ex) 예약프로그램

동시에 접속한 여러 사용자가 남은 표가 몇 개인지 확인할 때, 전부 똑같은 정보를 공유받는 것.

(*멀티스레드는 메모리 공간 공유. 변수 같은 것 공유)

 

(2) 멀티 스레드의 수행 방식

(2-1) 순차(sequential)

작업A → 작업B

 

(2-2) 동시(Concurrency)

 작업 단위를 잘게 쪼개서 들어갔다 나왔다 반복하며 작업 수행.

 작업1 일부 진행, 작업 2 일부 진행 다시 작업 1 일부 진행, 작업 2 일부 진행하는데 그 간격이 짧아 동시처럼 느껴짐.

 프로그램의 용량이 클 경우, 메모리에 전부 로딩되는 것이 아니라 일부만 올라가 교체되며 로딩.

 

(2-3) 병렬(parallelism) : 작업1과 작업2를 동시 진행

  (*작업수 < cpu 코어수)

 


 

2. 스레드 생성 및 실행방법

-스레드(Thread)

프로세스 내에서 실행되는 흐름 단위. 1회용. 종료되면 다시 사용할 수 없음. 새로 만들어야 함.

 

(1) 생성방법 1 : Thread 클래스 상속

스레드 클래스 상속받아 run() 메소드 오버라이딩(재정의)하여 사용

 

_예시) 자막과 비디오 동시 출력

-자막 스레드와 비디오 스레드 생성스레드 클래스를 상속받아 run() 메소드를 오버라이딩(재정의)함.

//1. 자막 스레드 생성
package aa;
public class MyThread extends Thread { //Thread 상속
	@Override
	public void run() { //run method 오버라이딩
		String[] strArray = new String[] {"one", "two", "three", "four", "five"};
		try {
			Thread.sleep(10);  //0.01초 일시정지
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		for(int i=0; i<strArray.length; i++) {
			System.out.println("(자막)"+strArray[i]);
			try {
				Thread.sleep(200);	 //자막 출력 후 0.2초 일시정지
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
//2. 비디오 스레드 생성
package aa;
public class VideoThread extends Thread { //Thread 상속
	@Override
	public void run() { //run method 오버라이딩
		int[] intArray = new int[] {1, 2, 3, 4, 5};
		for(int i=0; i<intArray.length; i++) {
			System.out.print("(비디오 프레임)"+intArray[i]+"-");
			try {
				Thread.sleep(200);    //비디오프레임 i 출력 후 0.2초 일시정지
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

 

-스레드 테스트

package aa;
public class ThreadTest {
	public static void main(String[] args) {
		Thread myThread = new MyThread();
		myThread.start(); //자막스레드 실행
		Thread videoThread = new VideoThread();
		videoThread.start();  //비디오스레드 실행

//		myThread.start(); //생성한 스레드는 한번만 실행할 수 있다.
	}
}

자막 스레드 시작할 때 0.01초 간격(자막 스레드에서 설정)을 두었기 때문에 비디오 스레드가 먼저 출력됨.

(비디오 프레임)1-(자막)one
(비디오 프레임)2-(자막)two
(비디오 프레임)3-(자막)three
(비디오 프레임)4-(자막)four
(비디오 프레임)5-(자막)five

-스레드 추가

package aa;
public class ThreadTest {
	public static void main(String[] args) {
		Thread myThread = new MyThread();
		myThread.start();  //1. 자막 스레드
		Thread videoThread = new VideoThread();
		videoThread.start();  //2. 비디오 스레드
		
		//3. 메인스레드 추가
		String[] strArray = new String[] {"one", "two", "three", "four", "five"};
		for(int i=0; i<strArray.length; i++) {
			System.out.println("(Main)"+strArray[i]);
			try {
				Thread.sleep(200);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
(비디오 프레임)1-(Main)one
(자막)one
(비디오 프레임)2-(Main)two
(자막)two
(비디오 프레임)3-(Main)three
(자막)three
(비디오 프레임)4-(Main)four
(자막)four
(비디오 프레임)5-(Main)five
(자막)five

 

(2) Runnable interface 구현

현재 클래스가 이미 다른 클래스로부터 상속받고 있다면 Runnable interface를 이용하여 스레드 생성

새로운 스레드가 아니라, 스레드(Thread) 안에 넣는 것. 러너블은 안드로이드에서 메인 스레드에 있는 UI 접근 가능.

 

-자막 스레드, 비디오 스레드 

Runnable interface 구현한 클래스 정의 : 추상메소드 run() 구현

//1. 자막 스레드 생성
package aa;
public class SMFileRunnable implements Runnable {
	//추상메소드 구현해줘야 오류 제거
	@Override
	public void run() {
		String[] strArray = new String[] {"one", "two", "three", "four", "five"};
		try {
			Thread.sleep(10);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		for(int i=0; i<strArray.length; i++) {
			System.out.println("(자막)"+strArray[i]);
			try {
				Thread.sleep(200);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
//2. 비디오 스레드 생성
package aa;
public class VideoFileRunnable implements Runnable {
	//추상메소드 구현
	@Override
	public void run() {
		int[] intArray = new int[] {1, 2, 3, 4, 5};
		for(int i=0; i<intArray.length; i++) {
			System.out.println("(비디오 프레임)"+intArray[i]+"-");
			try {
				Thread.sleep(200);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}		
	}
}

-테스트

자막, 비디오 클래스의 객체 생성 후 변수(?)를 스레드 생성자의 매개변수로 전달. 

package aa;
public class ThreadTest02 {
	public static void main(String[] args) {
		SMFileRunnable smiRunnable = new SMFileRunnable();
		VideoFileRunnable videoRunnable = new VideoFileRunnable();
		
		//start(); 못함
		//Thread로 객체 생성
		Thread smiThread = new Thread(smiRunnable);
		Thread videoThread = new Thread(videoRunnable);
        
		smiThread.start();   //실행
		videoThread.start(); //실행
	}
}

Runnable interface를 구현한 자막 클래스와 비디오 클래스의 객체를 생성한 것만으로는 Thread 실행 못함.

자막 클래스와 비디오 클래스의 객체를 생성하고 변수를 스레드 생성자(?) 객체(?)의 매개 변수로 전달.

 

(3) 스레드를 익명으로 생성

메인 함수 클래스에서 바로 익명클래스 만들어 사용.

객체 생설할 때, 매개변수 안에 익명클래스

package aa;
public class ThreadTest03 {
	public static void main(String[] args) {
		//자막스레드     //익명클래스 생성
		Thread myThread = new Thread(new Runnable() {
			@Override
			public void run() {
				String[] strArray = new String[] {"one", "two", "three", "four", "five"};
				try {
					Thread.sleep(10);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				for(int i=0; i<strArray.length; i++) {
					System.out.println("(자막)"+strArray[i]);
					try {
						Thread.sleep(200);
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
				}
			}	
		});
        
		//익명 비디오프레임 스레드 생성
		Thread myThread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				int[] intArray = new int[] {1, 2, 3, 4, 5};
				for(int i=0; i<intArray.length; i++) {
					System.out.print("(비디오 프레임)"+intArray[i]+"-");
					try {
						Thread.sleep(200);
					} catch (Exception e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			}	
		});
        //스레드 실행
		myThread.start();
		myThread2.start();
	}
}

-변수없이 실행시키는 경우

package aa;
public class ThreadTest03 {
	public static void main(String[] args) {
		//변수없이 바로 연결
		new Thread(new Runnable() {
			@Override
			public void run() {
				String[] strArray = new String[] {"one", "two", "three", "four", "five"};
				try {
					Thread.sleep(10);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				for(int i=0; i<strArray.length; i++) {
					System.out.println("(자막)"+strArray[i]);
					try {
						Thread.sleep(200);
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
				}
			}	
		}).start();  //변수 없이 바로 연결

		new Thread(new Runnable() {
			@Override
			public void run() {
				int[] intArray = new int[] {1, 2, 3, 4, 5};
				for(int i=0; i<intArray.length; i++) {
					System.out.print("(비디오 프레임)"+intArray[i]+"-");
					try {
						Thread.sleep(200);
					} catch (Exception e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			}
		}).start();  //변수 없이 바로 연결
	}
}

세미콜론(;) 생략하고 .start();바로 연결


3. 스레드 속성

(1) 스레드 객체 가져오기

-currentThread() : 현재 스레드

-activeCount() : 현재 돌고 있는 스레드 개수

package aa;
public class ThreadTest04 {
	public static void main(String[] args) {
		Thread curThread = Thread.currentThread();
		System.out.println("현재 스레드 이름 : "+curThread.getName());
		System.out.println("Thread 수 : "+Thread.activeCount());
		//특정 스레드를 생성하지 않았을때, 메인 스레드 존재(static)
	}
}
현재 스레드 이름 : main
Thread 수 : 1

 

(2) 이름 자동 설정 / 이름 직접 설정

getName() / setName()

package aa;
public class ThreadTest04 {
	public static void main(String[] args) {
		for(int i=0; i<5; i++) {
			Thread thread = new Thread();
			thread.setName(i+"번째 스레드");            //스레드 이름 직접 설정
			System.out.print(thread.getName()+" ");  //스레드 이름 자동 설정
			System.out.print(thread.getId()+" ");       //스레드 아이디
			System.out.println(thread.getPriority()); //thread 우선수위 : 높을수록 좋아요.
			thread.start();
		}
	}
}
0번째 스레드 14 5
1번째 스레드 15 5
2번째 스레드 16 5
3번째 스레드 17 5
4번째 스레드 18 5

└직접 설정한 스레드 이름 / 스레드 Id / 스레드 우선순위

(스레드 이름을 직접 설정했기 때문에 자동 설정한 것은 출력되지 않음? 가렸음?)

 

package aa;
public class ThreadTest04 {
	public static void main(String[] args) {
		Thread curThread = Thread.currentThread();
		System.out.println("현재 스레드 Id : "+curThread.getId());
		System.out.println("현재 스레드 이름 : "+curThread.getName());
		System.out.println("Thread 수 : "+Thread.activeCount());
		//특정 스레드를 생성하지 않았을때, 메인 스레드 존재(static)
		//메인스레드는 id 1
		
		for(int i=0; i<3; i++) {  
			Thread thread = new Thread();  //2
			thread.setName(i+"번째 스레드");            //스레드 이름 직접 설정
			System.out.print(thread.getName()+" ");  //스레드 이름 자동 설정
			System.out.print(thread.getId()+" ");       //스레드 아이디 // 14, 15, 16
			System.out.println(thread.getPriority()); //thread 우선수위 : 높을수록 좋아요. //5
			thread.start();
		}
		for(int i=0; i<3; i++) {
			Thread thread = new Thread();  //3
			thread.setName(i+"번째 스레드");            //스레드 이름 직접 설정
			System.out.print(thread.getName()+" ");  //스레드 이름 자동 설정
			System.out.print(thread.getId()+" ");       //스레드 아이디 // 14, 15, 16
			System.out.println(thread.getPriority()); //thread 우선수위 : 높을수록 좋아요. //5
			thread.start();
		}
		System.out.println("thread 수 : "+Thread.activeCount()); //thread 수 : 2
	}
}
현재 스레드 Id : 1
현재 스레드 이름 : main
Thread 수 : 1
0번째 스레드 14 5
1번째 스레드 15 5
2번째 스레드 16 5
0번째 스레드 17 5
1번째 스레드 18 5
2번째 스레드 19 5
thread 수 : 2

└스레드 수가 2로 출력되는 이유는, 스레드 수를 카운트하는 시점에서 앞선 스레드는 종료되었기 때문에.

 

(4) 스레드 우선순위

getPriority() : 우선순위 가져오기

setPriority() :  우선순위 설정하기 - 10(가장 높음) - 5(기본값) - 1(가장 낮은 우선순위)

package bb;
public class ThreadTest {
	public static void main(String[] args) {
		System.out.println("코어 수 : "+Runtime.getRuntime().availableProcessors());
		for(int i=0; i<3; i++) {
			Thread th = new MyThread();
			th.start();
		}
		try {
			Thread.sleep(1000);//1초
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for(int i=0; i<20; i++) {
			Thread th = new MyThread();
			th.setName(i+"번째 스레드");
			if(i==9) {
				th.setPriority(Thread.MAX_PRIORITY);  //9번째 스레드 우선순위 MAX
			}else {
				th.setPriority(Thread.MIN_PRIORITY);
			}
			th.start();
		}
	}
}

└스레드 우선순위 설정 : 인덱스 9를 가장 높은 우선순위로 둔다.

근데 실제 출력에서 9번째 스레드가 가장먼저 출력되지 않는데...그것은 이미 앞의 것들이 빠르게 처리되어버려서(?)

더보기
코어 수 : 4
Thread-0 우선순위 : 5
Thread-2 우선순위 : 5
Thread-1 우선순위 : 5
2번째 스레드 우선순위 : 1
1번째 스레드 우선순위 : 1
0번째 스레드 우선순위 : 1
6번째 스레드 우선순위 : 1
4번째 스레드 우선순위 : 1
3번째 스레드 우선순위 : 1
5번째 스레드 우선순위 : 1
9번째 스레드 우선순위 : 10
19번째 스레드 우선순위 : 1
18번째 스레드 우선순위 : 1
10번째 스레드 우선순위 : 1
8번째 스레드 우선순위 : 1
12번째 스레드 우선순위 : 1
7번째 스레드 우선순위 : 1
13번째 스레드 우선순위 : 1
14번째 스레드 우선순위 : 1
11번째 스레드 우선순위 : 1
15번째 스레드 우선순위 : 1
16번째 스레드 우선순위 : 1
17번째 스레드 우선순위 : 1

 

(5) 스레드 데몬설정

-일반 스레드 : 다른 스레드 종료 여부와 상관없이 자신의 스레드가 종료되어야 프로세스 종료

-데몬 스레드 : 일반 스레드(사용자 스레드)가 모두 종료되면 데몬 스레드의 작업이 완료되지 않았어도 함께 종료.

 (Thread 클래스의 인스턴스 메소드)

 setDaemon()은 start() 전에 호출되어야 한다.

void setDaemon(boolean on)
            //on - true : Daemon thread
            //default = false : 일반 스레드

 

package bb;
public class MyThread2 extends Thread {
	@Override
	public void run() {
		System.out.println(getName()+":"+(isDaemon()?"데몬스레드":"일반스레드"));
		for(int i=0; i<6; i++) {
			System.out.println(getName()+":"+i+"초");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

-일반스레드 예시

package bb;
public class ThreadTest02 {
	public static void main(String[] args) {
		
		Thread th1 = new MyThread2();
		th1.setDaemon(false); //true면 데몬스레드란 뜻
		th1.setName("thread1");
		th1.start(); 
		
		try {
			Thread.sleep(3000); //3초 정지
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//3초 정지 후 메인스레드 종료
		System.out.println("main thread end");
	}
}

-데몬스레드 예시

package bb;
public class ThreadTest02 {
	public static void main(String[] args) {
		
		//Daemon thread
		Thread th2 = new MyThread2();
		th2.setDaemon(true); //true면 데몬스레드란 뜻
		th2.setName("thread1");
		th2.start(); 
		
		try {
			Thread.sleep(3000); //3초 정지
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//3초 정지 후 메인스레드 종료
		System.out.println("main thread end");
	}
}

 

-일반스레드와 데몬스레드가 섞여있을때

package bb;
public class ThreadTest02 {
	public static void main(String[] args) {
		
		Thread th1 = new MyThread2();
		th1.setDaemon(false); //true면 데몬스레드란 뜻
		th1.setName("thread1");
		
		//Daemon thread
		Thread th2 = new MyThread2();
		th2.setDaemon(true); //true면 데몬스레드란 뜻
		th2.setName("thread2");
		
		th1.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		th2.start();
		
		try {
			Thread.sleep(3000); //3초 정지
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//3초 정지 후 메인스레드 종료
		System.out.println("main thread end");
	}
}
thread1:일반스레드
thread1:0초
thread1:1초
thread2:데몬스레드
thread2:0초
thread1:2초
thread2:1초
thread1:3초
thread2:2초
main thread end
thread1:4초
thread2:3초
thread1:5초
thread2:4초

4. Thread 동기화

하나의 작업이 완전히 완료된 후 다른 작업 수행. 순차적 실행. ex) 티켓 예매

↔ 비동기식 : 하나의 작업 명령 이후(완료와 상관없이) 바로 다른 작업 명령을 수행

└웹페이지 로그인 방식. 메인 페이지는 그대로 있고 로그인 상태만 변경.

 

-비동기화 상태

-MyData와 PlustData

package bb;
public class MyData {
	int data = 3;
	 public void plusData() {
		 int mydata = data;
		 try {
			Thread.sleep(2000);  
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		 data = mydata + 1; //2초 정지 후 대입
	 }
}
//PlusThread
package bb;
public class PlusThread extends Thread {
	MyData myData;
	public PlusThread(MyData myData) {
		this.myData = myData;
	}
	@Override
	public void run() {
		myData.plusData();
		System.out.println("실행결과 : " +myData.data);
	}
}

 

package bb;
public class PlusThreadTest {
	public static void main(String[] args) {
		//비동기화 상태
		// plusThread1
		MyData myData = new MyData();
		Thread plusThread1 = new PlusThread(myData);
		plusThread1.setName("plusThread1");
		plusThread1.start();
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//plusThread2
		Thread plusThread2 = new PlusThread(myData);
		plusThread2.setName("plusThread1");
		plusThread2.start();
	}
}

 

(1) 동기화 방법_1 : 메소드 동기화

메소드에 synchronized 입력

package bb;
public class MyData {
	int data = 3;
	 public synchronized void plusData() {
		 int mydata = data;
		 try {
			Thread.sleep(2000);  
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		 data = mydata + 1; //2초 정지 후 대입
	 }
}

 

(2) 동기화 방법_2 : 블록 동기화

package bb;
public class MyData {
	int data = 3;
	 public void plusData() {
		 synchronized(this){  //블록 동기화
			 int mydata = data;
			 try {
				 Thread.sleep(2000);  
			 } catch (InterruptedException e) {
				 e.printStackTrace();
			 }
			 data = mydata + 1; //2초 정지 후 대입
		 }//synchronized
	}
}

 

(3) 동기화 원리

모든 객체는 단 하나의 열쇠를 가지고 있음.

동기화(synchnoized)를 사용하면 처음 사용하는 Thread가 Key객체의 Key를 가짐

다른 스레드는 먼저 사용 중인 스레드가 작업을 완료하고 key를 반납할 때까지 대기(Blocked)

 

-각각 동기화했기 때문에 순차적 실행.

abc(), bcd(), cde()는 동기화된 상태라 순차적 실행.

package bb;
public class MyData2 {
	synchronized void abc() {  //synchronized method
		for(int i = 0; i<3; i++) {
			System.out.println(i+"sec");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	synchronized void bcd() {  //synchronized method
		for(int i = 0; i<3; i++) {
			System.out.println(i+"초");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	void cde() {  
		synchronized (this) {  //synchronized block
			for(int i = 0; i<3; i++) {
				System.out.println(i+"번째");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}//Mydata

└key : 똑같음. this와 ()은 같은 것. 

-메인 함수에서 객체 생성 및 출력

synchronized( key ) 동기화 묶는 방법

package bb;
public class ThreadTest03 {
	public static void main(String[] args) {
		MyData2 myData = new MyData2();
		
		new Thread() {
			public void run() {
				myData.abc();
			};
		}.start();  //변수없이 바로 연결
		
		new Thread() {
			public void run() {
				myData.bcd();
			};
		}.start();  //변수없이 바로 연결

		new Thread() {
			public void run() {
				myData.cde();
			};
		}.start();  //변수없이 바로 연결
	}

}
더보기
0sec
0번째
1sec
1번째
2sec
2번째
0초
1초
2초

-동기화, 비동기화 섞였을 때.

package bb;
public class MyData2 {
	synchronized void abc() {  //synchronized method
		for(int i = 0; i<3; i++) {
			System.out.println(i+"sec");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	synchronized void bcd() {  //synchronized method
		for(int i = 0; i<3; i++) {
			System.out.println(i+"초");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	void cde() {  //synchronized block
		synchronized (new Object()) {  //key - new Object().객체가 다르다는 것은 참조 위치가 다르다는 거고 동기화 안 되서 섞임.
			for(int i = 0; i<3; i++) {
				System.out.println(i+"번째");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}//Mydata

abc() {...}와 bcd(){...}는 키값이 같음. 동기화. but) cde(new Object()){...}로 키값이 달라 비동기화 상태.따라서 동기화 상태인 abc와 bcd는 순차적으로 실행되지만 cde는 동시 실행돼 섞여서 출력됨.

더보기
0sec
0번째
1sec
1번째
2sec
2번째
0초
1초
2초

 

5. thread 상태(state)

 

NEW
스레드 객체 생성
RUNNABLE
실행대기 ↔ 실행
TERMINATED
종료
TIMED WAITING / BLOCKED / WAITING

 

(1) getState()

스레드 상태 확인

Thread.getState();

└ NEW | RUNNABLE | TIMED WAITING | BLOCKED | WAITING | TERMINATED

 

_스레드 상태 확인 예시)

package bb;
public class ThreadStateTest {
	public static void main(String[] args) {
		Thread.State state;  //변수 생성

		Thread thread = new Thread() { //구현
			public void run() {
				for(int i=0; i<10000000L; i++) {
				}
			}
		};//thread 생성된 상태
		state = thread.getState();
		System.out.println("thread state : "+state);  //new
		
		thread.start();//thread 실행
		
		state = thread.getState();
		System.out.println("thread state : "+state);  //RUNNABLE
		try {
			thread.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		state = thread.getState();
		System.out.println("thread state : "+state);  //TERMINATED
	}
}

 

(2) 스레드 양보(yield)

현재 스레드가 다른 스레드에게 cpu(실행) 양보하고 자신은 대기상태

public class MyThread extends Thread{
	boolean yieldFlag;	
	@Override
	public void run() {
		while(true) {
			if(yieldFlag) {
				Thread.yield(); //true일 때, 양보
			}else {
				System.out.println(getName()+"실행");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

-메인 함수에서 출력

public class ThreadYieldTest {
	public static void main(String[] args) {
		MyThread thread1 = new MyThread();
		thread1.setName("thread1");
		thread1.yieldFlag = false;
		thread1.setDaemon(true);
		thread1.start();
		
		MyThread thread2 = new MyThread();
		thread2.setName("thread2★");
		thread2.yieldFlag = true;
		thread2.setDaemon(true);
		thread2.start();
		
		for(int i =0; i<6; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			thread1.yieldFlag = !thread1.yieldFlag;  //1초마다 위, 아래 바뀜.왔다갔다.
			thread2.yieldFlag = !thread2.yieldFlag;
		}
	}
}
thread1실행
thread1실행
thread2★실행
thread2★실행
thread1실행
thread1실행
thread1실행
thread2★실행
thread2★실행
thread1실행
thread1실행
thread2★실행
thread1실행
thread2★실행
thread1실행

 

(3) TIMED_WAITING

일시정지 상태. CPU 사용 불가. 정해진 시간이 종료되거나 interrupt() 발생 시 RUNNABLE 상태로 전환.

interrupt() 일시정지 상태 탈출

 

(3-1) sleep - interrupt

package cc;
public class MyThread extends Thread{
	@Override
	public void run() {
		try {
			Thread.sleep(3000); //3초간 정지
		} catch (InterruptedException e) { //인터럽트가 있다면 catch문으로 들어감.
			System.out.println("--sleep() 중 interrupt 발생--");
			try {
				Thread.sleep(100);
			} catch (InterruptedException e1) {	
			}
		}
	}
}
//메인 함수에서 TEST
package cc;
public class ThreadTest {
	public static void main(String[] args) throws InterruptedException {
		MyThread myThread = new MyThread();
		myThread.start();
		
		Thread.sleep(100);
		System.out.println("MyThread state : "+myThread.getState());
		
		myThread.interrupt();  //--sleep() 중 interrupt 발생--
		Thread.sleep(100);
		System.out.println("MyThread state : "+myThread.getState());
	}
}

interrupt() 호출하면, Thread에서 InterruptException 예외 발생시켜 예외처리. 일시정지 탈출.

package cc;
public class ThreadTest {
	public static void main(String[] args) throws InterruptedException {
		MyThread myThread = new MyThread();
		myThread.start();
		
		Thread.sleep(100);
		System.out.println("MyThread state : "+myThread.getState());
		
		myThread.interrupt();  //--sleep() 중 interrupt 발생--
		Thread.sleep(100);
		System.out.println("MyThread state : "+myThread.getState());
	}
}

 

(3-2) join - interrupt

package cc;
public class MyThread1 extends Thread {
	@Override
	public void run() {
//		for(long i=0; i<1000000000L; i++) { } //처리속도가 빨라서 멈추는 상태 확인이 잘 안 됨.
		for(;;) {
		}
	}
}
//
class MyThread2 extends Thread{
	MyThread1 myThread1;
	
	public MyThread2(MyThread1 myThread1) {
		this.myThread1 = myThread1;
	}
	
	@Override
	public void run() {
		try {
			myThread1.join(3000);  //3초간 cpu 쓰겠다.
		} catch (InterruptedException e) {
			System.out.println("--join() 중 interrupt 발생--");  //인터럽트 발생 시 실행
		}
//		for(long i=0; i<1000000000L; i++) { } //처리속도가 빨라서 멈추는 상태 확인이 잘 안 됨.
		for(;;) {}
	}
}
package cc;
public class MyTread1Test {
	public static void main(String[] args) throws InterruptedException {
		//1
		MyThread1 myThread1 = new MyThread1();
		MyThread2 myThread2 = new MyThread2(myThread1);
		myThread1.start();
		myThread2.start();
		
		Thread.sleep(100);  //메인 스레드 딜레이
		System.out.println("MyThread1 State : "+ myThread1.getState());  //RUNNABLE
		System.out.println("MyThread2 State : "+ myThread2.getState());  //TIMED_WAITING
		
		myThread2.interrupt(); //--join() 중 interrupt 발생--
		Thread.sleep(100);
		System.out.println("MyThread1 State : "+ myThread1.getState());  //RUNNABLE
		System.out.println("MyThread2 State : "+ myThread2.getState());  //RUNNABLE
	}
}

 

*싱크로나이즈로 해결할 수 없는 동기화 문제 해결하는 방법

 

(4) blocked

싱크로나이즈일 때 : cpu를 사용중인 스레드가 runnable, 그 외의 스레드는 block.

일정시간 일시정지상태를 나타내며 이 상태에서는 CPU를 사용할 수 없음.

사용객체의 Lock이 풀리고 사용을 위한 Key를 획득하면 Runnable 상태로 전환.

package cc;

class MyBlockTest1{
	MyClass mc = new MyClass(); //공유 객체
	
	Thread t1 = new Thread("thread1") {
		public void run() {mc.syncMethod();}
	};
	Thread t2 = new Thread("thread2") {
		public void run() {mc.syncMethod();}
	};
	Thread t3 = new Thread("thread3") {
		public void run() {mc.syncMethod();}
	};
	void startAll() {
		t1.start(); t2.start(); t3.start();
	}
	class MyClass{
		synchronized void syncMethod() {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				
			}
			System.out.println("["+Thread.currentThread().getName()+"]"); //이 스레드에서
			System.out.println("thread1 >>"+t1.getState()); //thread1 상태
			System.out.println("thread2 >>"+t2.getState()); //thread2 상태
			System.out.println("thread3 >>"+t3.getState()); //thread3 상태
		}
	}
}//MyBlockTest1
//메인 함수 : 객체 생성 및 출력
public class MyBlockTest {
	public static void main(String[] args) {
		MyBlockTest1 mbt = new MyBlockTest1();
		mbt.startAll();
	}
}

└어떤 스레드가 진행 중일 때 나머지 스레드의 상태를 확인하는 것.

더보기
[thread1]
thread1 >>RUNNABLE
thread2 >>BLOCKED
thread3 >>BLOCKED
[thread3]
thread1 >>TERMINATED
thread2 >>BLOCKED
thread3 >>RUNNABLE
[thread2]
thread1 >>TERMINATED
thread2 >>RUNNABLE
thread3 >>TERMINATED

(*스레드 여러 개 만들 때, 실행순서는 뒤바뀔 수 있음. 만든 순서대로 시행 안 될 수도.)

(*여러 개의 스레드를 실행할 때, 원하는 순서대로 실행시키는 방법 : notify();, notifyAll();)

 

(4) notify()

동기화된 메소드, 동기화된 블록 내에서만 사용 가능. Object 메소드.

notify()는 하나의 쓰레드를, notifyAll()는 wait() 중인 모든 Thread를 깨움.

waiting 중인 메소드를 notify()로 깨우는 것. 실행시키는 것.

 

-스레드 / 데이터 입력 및 출력(메인 함수) : notify 안 한 상태.

package cc;
public class DataBox {
	int data;
	synchronized void inputData(int data) throws InterruptedException { //예외전가
		this.data = data;
		System.out.println("입력데이터 : "+data);
	}
	
	synchronized void outputData() throws InterruptedException{
		System.out.println("출력데이터 : "+data);
	}
}

//메인함수 클래스
package cc;
public class ThreadTest02 {
	public static void main(String[] args) {
		DataBox dataBox = new DataBox();
		Thread t1 = new Thread() { //쓰기 스레드 - 입력
			public void run() {
				for(int i = 0; i<10; i++) {
					try {
						dataBox.inputData(i);
					} catch (InterruptedException e) { }
				}
			}
		};
		
		Thread t2 = new Thread() {  //읽기 스레드 - 출력
			public void run() {
				for(int i = 0; i<10; i++) {
					try {
						dataBox.outputData();
					} catch (InterruptedException e) { }
				}
			}
		};
		t1.start();
		t2.start();
	}
}

↓waiting WithWaitNotify ↓

- 쓰기 스레드 동작(데이터 출력)

- 읽기 쓰레드 깨우기 notify()

- 쓰기 쓰레드 일시정지 wait()

package cc;
public class DataBox {
	boolean isEmpty = true; //기본값이 null이니까.
	int data;
	synchronized void inputData(int data) throws InterruptedException { //예외전가
		if(!isEmpty) { //isEmpty 데이터가 있으면 → false일 때,  wait
			wait();
		}isEmpty = false;  //isEmpty를 false로 바꿈.
		this.data = data;
		System.out.println("입력데이터 : "+data);
		notify(); // 스레드 하나를 깨움.
	}
	
	synchronized void outputData() throws InterruptedException{
		if(isEmpty) {
			wait();
		}
		isEmpty = true;
		System.out.println("출력데이터 : "+data);
		notify();	
	}
}

 

END