스레드

Java

목록 보기
19/26
post-thumbnail

1. 프로세스와 스레드

  • 프로세스 : 실행 중인 프로그램, 자원과 스레드로 구성
  • 스레드 : 프로세스 내에서 실제 작업ㅇ르 수행. 모든 프로세스는 최소한 하나의 스레드를 갖고 있음.
    프로세스 : 스레드 = 공장 : 일꾼
  • 싱글 스레드 프로세스 : 자원 + 스레드
  • 멀티 스레드 포르세스 : 자원 + 스레드 + 스레드 + 스레드 + ... +스레드
  • 멀티 스레드의 단점
    동기화에 주의해야 한다.
    교착상태가 발생하지 않도록 주의해야 한다.

2. 스레드의 구현과 실행

2-1. Thread 클래스를 상속

  • Thread 클래스의 run()을 오버라이딩
class MyThread extends Thread {
	public void run() {
    }
}
Mythread t = new Myth();
t.start();

2-2. Runnable 인터페이스를 구현

  • Runnable 인터페이스의 추상메서드 run()을 구현
  • 권장 방법
class MyThread implements Runnable{
	public void run() {
    }
}
Runnable r = new MyThread();
Thread t = new Thread(r);			//Thread(Runnable r)
t.start();
Thread t = new Thread(new MyThread());

2-3. 스레드의 실행 - start()

  • 스레드를 생성한 후 start()를 호출해야 스레드가 작업을 시작한다.
  • start()를 하면 실행 가능한 상태가 되는거지 바로 실행되는게 아니다. 실행하는건 OS 스케쥴러가 결정한다.
    그랫 t1.start()와 t2.start() 중 뭐가 먼저 실행될지는 모름
ThreadEx1_1 t1 = new ThreadEx1_1();
ThreadEx1_1 t2 = new ThreadEx1_1();
t1.start();
t2.start();

3. main 스레드

-main 스레드 : main 메서드의 코드를 수행하는 스레드

  • 스레드는 사용자 스레드데몬 스레드(보조 스레드)가 있다
  • 보조스레드는 보조 스레드라서 실행 중이더라도 프로그램 종료가 된다.
  • 실행 중인 사용자 스레드가 하나도 없을 떄 프로그램은 종료된다.
Thread MyThread t1 = new Thread();
t1.start();

for (int i=0; i<300; i++) {
	System.out.println("-");
}

메인스레드와 t1 스레드가 함께 섞여서 출력됨.
main(String args[]) 이게 메인 스레드라는 것!

4. 싱글 스레드와 멀티스레드

  • 싱글 스레드
class ThreadTest {
	public static void main(String args[]) {
    	for(int i=0; i<300, i++) {
        	System.out.println("_");
        }
        for(int i=0; i<300, i++) {
        	System.out.println("|");
        }
    }
}



싱글 스레드로 돌릴 때는 두 작업이 절대 겹치지 않음

  • 멀티스레드
class ThreadTest{
	public static void main(String args[]) {
    	MyThread t1 = new MyThread();
        MyThread2 t2 = new MyThread2();
        t1.start();
        t2.start();
    }
}
class MyThread exteds Thread() {
	public void run() {
    	for(int i=0; i<300;i++) {
        	System.out.println("-");
        }
    }
}
class MyThread2 exteds Thread() {
	public void run() {
    	for(int i=0; i<300;i++) {
        	System.out.println("|");
        }
    }
}

멀티 스레드일 경우임. 이 때는 두 작업이 겹침. 어떤 작업이 먼저 시작할지는 모름

5. 스레드의 I/O 블락킹(blocking)

  • Input / Output 블락킹
  • 싱글 스레드일 때
class ThreadEx{
	String input = JOptionPane.showInputdialog("아무 값이나 입력하세요.");
    System.out.println("입력하신 값은" + input + "입니다.");
    
    for (int i=0;; i>0; i--) {
    	System.out.println(i);
        try(Thread.sleep(1000)
        }
    }
}

  • 멀티 스레드일 때
class ThreadEx1{
	public static void main(String args[]) {
    THreadEx1 th1 = new ThreadEx1();
    th1.start();
    
    String input = JOptionPane.showInputdialog("아무 값이나 입력하세요.");
    System.out.println("입력하신 값은" + input + "입니다.");
   }
}
class ThreadEx2 extends Thread{
	public void run() {
    	for(int i=0; i>0; i--) {
        	System.out.println(i);
            try{sleep(1000);} catch(Exception e) {}
        }
    }
}

6. 스레드의 우선순위

  • 작업의 중요도에 따라 스레드의 우선순위를 다르게 하여 특정 스레드가 더 많은 작업시간을 갖게 할 수 있다.
    void setPriority(int newPriority) : 스레드의 우선순위를 지정한 값으로 변경한다
    int getPriority() : 스레드의 우선순위를 반환한다.
public static final int MAX_PRIORITY = 10;
public static final int MIN_PRIORUTY = 1;
public static final int NORM_PRIORITY = 5; 

우선순위를 지정하지 않으면 5로 지정됨.

  • 이론적으로는 우선순위가 높은 스레드가 더 많은 시간을 할당받음....이지만, 실제는 다름. 스레드의 우선순위는 단지 희망사항에 불과함.

7. 스레드 그룹

  • 서로 관련된 스레드를 그룹으로 묶어서 다루기 위한 것
  • 모든 스레드는 반드시 하나의 스레드 그룹에 포함되어 있어야 한다.
  • 스레드 그룹을 지정하지 않고 생성한 스레드는 main스레드 그룹에 속한다
  • 자신을 생성한 스레드(부모 스레드)의 그룹과 우선순위를 상속받는다.

    이런게 있다는 정도만 아세요.

7-1. 스레드 그룹의 메서드


깊이 있게 아려고 하지 마시고 스레드가 그룹으로 관리된다는 것 정도 알아두시기 바랍니다.

8. 데몬스레드

  • 일반 스레드의 작업을 돕는 보조적인 역할을 수행
  • 일반스레드가 모두 종료되면 자동적으로 종료된다.
  • 가비지 컬렉터, 자동저장, 화면 자동갱신 등에 사용된다.
  • 무한루프와 조건문을 이용해서 실행 후 대기하다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.
public void run() {
	while(true) {					//무한루프
    	try {
        	Thread.sleep(3*1000)'
        } catch(InterruptedException e) {}
    if(autoSave) {					//특정 조건일 때 실행
    		autoSave();
    }
    }
}
boolean isDaemon()    - 스레드가 데몬스레디인지확인한다. 데몬스레드면 true 반환
void setDaemon(boolean on) - 스레드를 데몬 스레드로 또는 사용자 스레드로 변경. 매게변수 on을 true로 지정하면 데몬 스레드가 된다.
  • setDaemon(boolean on)은 반드시 start()를 호출하기 전에 실행되어야 한다. 그렇지 않으면 illegalThreadStateEception이 발생한다.
    "기본적으로는 데몬스레드 아닌 스레드로 만들어지니까 호출 할 일이 없겠죠."
    -> 데몬스레드로 만드려면 반드시 start()전에 호출하라는 뜻 인거같음.
public class Excercise13_7 implements Runnable {

	static boolean autoSave = false;
	
	public static void main(String[] args) {
		
		Thread t = new Thread(new Excercise13_7());		//Thread(Runnable r)
													//지금 이 클레스를 스레드의 생성자에 전달한다.
		t.setDaemon(true);							
		t.start();
		
		for (int i=1; i<=10; i++) {
			try {
				Thread.sleep(1000);
			} catch(InterruptedException e) {}
			System.out.println(i);
			if (i==5) autoSave =true;
		}
		
		System.out.println("프로그램을 종료합니다.");

	}

	public void run() {								//데몬스레드가 됨.
		while(true) {
			try {
				Thread.sleep(3*1000);
			} catch (InterruptedException e) {}
			if(autoSave) autoSave();
		}
	}
	public void autoSave() {
		System.out.println("작업파일일 자동 저장되었습니다.");
	}	
}

결과▼

9. 스레드의 상태

  • 스레드의 실행 제어

    static 메서드는 sleep() yied()가 있다. 특징 : 자기 자신한테만 동작한다.

9-1. sleep()

  • 현재 스레드를 지정된 시간동안 멈추게 한다.
  • static 메서드(자기 자신한테만 동작)
static void sleep(long millis)
static void sleep(long milles, int nanao)   //잘 안씀. 몰라도 됨.
  • 예외 처리를 해야 한다. (InterruptedException이 발생하면 깨어남)
    - 스레드가 멈추는 경우 1) time up(시간 종료) 2) interrupt(누가 깨움)
try {
	Thread.sleep(1,500000);
    } catch(InterruptedException이 e) {}		//안에 아무것도 안 써도 됨
}

항상 이렇게 예외처리를 해줘야하는데 불편하잖아요.
그러니까 메서드를 따로 만들어요. ex)delay sleep마세더를 다른 메서드 안에 넣은거에요.

void delay(long millis) {
	try{
    	Thread.sleep(millis);
        } catch(InterruptedException이 e) {}
    }

  • 특정 스레드를 지정해서 멈추게 하는 것은 불가능

    왼) 특정 스레드를 sleep하는 걸로 오해할 수 있기 떄문에 (본인도 실수할 수 있어서)
    오) 클래스 이름으로.

    이러면 main 스레드가 잠드는거에요.

    주석 처리하면, main스레드가 바로 종료되죠?

9-2. interrupt()

  • 대기상태(ex sleep(), wait(), join())인 스레드를 실행대기 상태(Runnable)로 만든다.

    😶static boolean interrupted()는 static이라 자기 자신한테 밖에 적용 안됨!
public class Ex13_9 {
	public static void main(String[] args) { 
		ThreadEx9_1 th1 = new ThreadEx9_1();
		th1.start();
		
		String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
		System.out.println("입력하신 값은" + input + "입니다");
		th1.interrupt();
		System.out.println("isinterrupted():" + th1.isInterrupted());
        System.out.println("interrupted():"+ th1.interrupted());  //main 스레드가 interrupt 됐는지😶
	}
}
class ThreadEx9_1 extends Thread{
	public void run() {
		int i = 10; 
		
		while(i!=0 && !isInterrupted()) {
			System.out.println(i--);
			for (long x =0; x <2500000000L; x++);
		}
		System.out.println("카운트가 종료되었습니다.");
	}
}



이 예제를 보면 알 수 있는게, interrupt가 스레드를 멈추게 하지 않음. 당시 interrupt 변수 상태를 바꿔서 그걸 이용해서 메서드를 종료하게 내가 메서드를 설계하는거임(interrupt 변수가 false가 되면 print그만해라).
흠...그럼 interrupt가 굳이 왜 있는지는 모르겠음...딱히 일시정지 상태도 아니지않나....흠....실무에서 써봐야 존재 이유가 이해가 가려나...

9-3. suspend(), resume(), stop()

  • 스레드의 실행을 일시정지, 재개, 완전정지 시킨다.
  • 교착상태에 빠지기 쉬움
  • 사용하지 않도록 권장입니다. 되도록 쓰지 마세요.

9-4. join()

  • 지정된 시간동안 특정 스레드가 작업하는 것을 기다린다.
  • 예외처리를 해야한다 (InterruptedException이 발생하면 작업 재개)
th1.join()   //`main스레드`가 `th1스레드`가 끝날 때까지 기다린다.

9-5. yield()

  • 남은 시간을 다음 스레드에게 양보하고, 자신9현재 스레드)은 실행대기한다.
  • yield()와 interrupted()를 적절히 사용하면, 응답성과 효율을 높일 수 있다.

10. 동기화

  • 멀티 스레드 프로세스는 다른 스레드의 작업에 영향을 미칠 수 있다.
  • 진행 중인 작업이 다른 스레드에게 간섭받지 않게 하려면 동기화 필요
  • 동기화 하려면 간섭받지 않아야 하는 문장들을 임계 영역으로 설정
  • 임계 영역은 락(lock)을 얻은 단 하나의 스레드만 출입가능(객체 1개에 락 1개)

10-1. synchronized로 임계영역(lock이 걸리는 영역)을 설정하는 방법 2가지

  1. 메서드 전체를 임계 영역으로 지정 (비추)
public synchronized void calcSum(){
}

그런데 임계 영역이 적을 수록 효율적이다. 최소화하는게 좋다.
2. 특정한 영역을 임계 영역으로 지정

synchronized(객체의 참조변수) {
}
public void withdraw(int money) {
	synchronized(this) {
    	if(balance >=money) {
        	try{
            	Thread.sleep(1000);
            } catch (exception e) {}
            balance -= money;
        }
    }
}
  • private으로 해야 동기화 의미가 있다.🔋

10-2. wait()와 notify()

  • 동기화의 효율을 높이기 위해 wait(), notify() 사용
  • Object 클래스에 정의되어 있으며, 동기화 블록 내에서만 사용할 수 있다.

    notify() : 기다리는 것 중 하나 아무거나 깨움
    notify() : 모두한테 알림
  • 요리사는 Table에 음식을 추가. 손님은 Table의 음식을 소비
  • 요리사와 손님이 같은 객체(Table)을 공유하므로 동기화가 필요
  • ArrayList는 동기화 안 되는거 아시죠?

wait과 notify할 사람이 불분명. 요리사? 손님?
음식이 없을 때 손님을 깨우려면?
Lock&condition

스레드 동기화 예제 1) 은행 출금

public class ThreadEx21 {

	public static void main(String[] args) {
		Runnable r= new RunnableEx21();				//RunnableEx의 객체를 1개 만듦
		Thread th1 = new Thread(r);					//그 객체를 th1 스레드로 공유함
		Thread th2 = new Thread(r);					//그 객체를 th2 스레드로 공유함
		th1.start();
		th2.start();
	}
}

runnable 클래스의 객체를 하나 만들고
그 하나의 객체로 스레드를 여러개 만드는구나....
객체 여러개 만들고 각 개체당 스레드를 만들지 않는구나...

class Account {
	
	private int balance = 1000;
	
	public int getBalance() {
		return balance;
	}
	
	public void withdraw(int money) {
		if(balance>=money) {
			try {Thread.sleep(1000);
			} catch(InterruptedException e) { }
			balance -= money;
		}
	}
}

withdraw 앞에 synchronized를 붙이면 동기화 된다.
단, private int balance로 해야 의미가 있다.

class RunnableEx21 implements Runnable {
	
	Account acc = new Account();
	
	public void run() {
		while(acc.getBalance() > 0) {
			// 100, 200, 300 중 임의의 값을 선택해서 출금(withdraw)
			int money = (int) (Math.random() * 3 +1 ) *100;
			acc.withdraw(money);
			System.out.println("balance:" + acc.getBalance());
		}
	}
}

lock 인쇄 이상하게 된 부분
stampedlock

0개의 댓글