고급JAVA 9강 - 쓰레드의 제어

Whatever·2021년 11월 9일
0

고급 JAVA

목록 보기
9/32

/*
10마리의 말들이 경주하는 프로그램을 작성하시오.

말은 Horse라는 이름의 쓰레드 클래스로 작성하는데
이 클래스는 말이름(String), 등수(int), 현재위치(int)를 멤버변수로 갖는다.
그리고 이 클래스에는 등수를 오름차순으로 정렬할 수 있는 내부 정렬 기준이 있다. (Compareable 인터페이스 구현)

경기 구간은 1 ~ 50구간으로 되어 있다.

경기가 끝나면 등수 순으로 출력한다.

경기 중에는 중간 중간에 각 말들의 위치를 아래와 같이 나타내 준다.
예)
말이름1 : --->---------------------------------------- (50개)
말이름2 : ------>------------------------------------- (50개)
...
말이름10 : ---->--------------------------------------- (50개)

50구간째가 '>'가 되면 끝남
*/
public class ThreadTest13 {

public static void main(String[] args) {
	Horse[] horses = new Horse[] {
			new Horse("1번말"),
			new Horse("2번말"),
			new Horse("3번말"),
			new Horse("4번말"),
			new Horse("5번말"),
			new Horse("6번말"),
			new Horse("7번말"),
			new Horse("8번말"),
			new Horse("9번말"),
			new Horse("10번말")
	};
	
	//경주 시작
	for(Horse hs : horses) {
		hs.start();
	}
	
	//2초에 한번씩
	outer : while(true) {
		for(Horse hs : horses) {
			System.out.print(hs.name);
			System.out.println(hs.race);
		}
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
		//모든 쓰레드가 끝났을 때 break
		int countThread = 0;
		for(Horse hs : horses) {
			if(hs.getState() == Thread.State.TERMINATED) {
				countThread++;
			}
			if(countThread == 10) {
				break outer;
			}
		}
		System.out.println("=====================================================");
		
	}
	
	
	
	//랭크 나타내기

	System.out.println(Horse.setRank);
	
	
}

}

class Horse extends Thread implements Comparable<Horse> {
    public static ArrayList<String> setRank = new ArrayList<>();
    public String name;
    private int rank;
    private int now;
    char[] race = new char[49];

//생성자
public Horse(String name) {
	this.name = name;
	rank = 0;
	now = 0;
	//50구간에서 한칸씩 증가
	for(int i = 0; i < 49; i++) {
		race[i] = '-';
	}
}

//비교 - 등수를 기준으로 오름차순
@Override
public int compareTo(Horse h) {
	return Integer.compare(rank, h.rank);
}

//달리는 메서드
@Override
public void run() {
	
	//현재 위치 나타내기
	for(int i = 0; i < 49; i++) {
		if(now == i) {
			race[i] = '>';
			//값을 묶기
			try {
				Thread.sleep((int)(Math.random() * 500));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			now++;
		}

// System.out.print(name + " : ");
// System.out.println(race);
race[i] = '-';
}
//끝난 순서대로 이름 집어넣기
setRank.add(name);
// System.out.println(setRank);
}

}

쓰레드의 제어

Thread의 stop()메서드를 호출하면 쓰레드가 바로 멈춘다.
이 때 사용하던 자원을 정리하지 못하고 프로그램이 종료되어
이 후에 실행되는 프로그램에 영향을 줄 수 있다.
그래서 stop()메서드는 비추천으로 되어 있다.
stop()메서드는 비추천됨

sleep() => 주어진 쓰레드가 일시정지됨
일시정지 상태의 쓰레드는 interrupt()를 만나면
interruptedException을 발생하며 빠져나옴

1.13 쓰레드의 동기화 - synchronized

  • 한번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 락(lock)을 걸어서
    데이터 일관성을 유지하는 것.
    1. 특정한 객체에 lock을 걸고자 할 때
    synchronized(객체의 참조변수){
    }
    2. 메서드에 lock을 걸고자 할 때
    public synchronized void calcSum(){
    }
public static void main(String[] args) {
//		ThreadStopTest1 th1 = new ThreadStopTest1();
//		th1.start();
//		
//		try {
//			Thread.sleep(1000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		//th1.setStop(true);
//		th1.stop();
	
    
	ThreadStopTest2 th2 = new ThreadStopTest2();
	th2.start();
	
	try {
		Thread.sleep(1000);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	th2.interrupt();
	
	
}

}

  // 쓰레드를 멈추게 하는 연습용 쓰레드
  class ThreadStopTest1 extends Thread{
      private boolean stop;

public void setStop(boolean stop) {
	this.stop = stop;
}

@Override
public void run() {
	while(!stop) {
		System.out.println("쓰레드 실행 중...");
	}
	
	System.out.println("자원 정리");
	System.out.println("쓰레드 종료");
}

}

// interrupt()메서드를 이용하여 쓰레드를 멈추게 하는 방법
class ThreadStopTest2 extends Thread{
    @Override
    public void run() {
        //방법1 ==> interrupt()메서드와 sleep()메서드를 이용하는 방법
	
	try {
		while(true) {
			System.out.println("실행 중");
			Thread.sleep(1); 
                   // 일시정지 상태에서 interrupt()메서드가 실행되면 interruptedException이 발생한다.
			// 그러면 catch문으로 빠져나온다. 
		}
	} catch (InterruptedException e) {

	}
	
	//방법2 ==> interrupt()메서드가 호출되었는지 검사하는 방법
	while(true) {
		System.out.println("Thread 실행중...");
		
		// interrupt() 메서드가 호출되었는지 여부를 검사한다.
		// 검사방법1) ==> Thread의 인스턴스 메서드인 isInterrupted()를 이용하여 검사하기
		// - isInterrupted()메서드 interrupt()메서드가 호출되면 true를 반환한다.
		//
		//if(this.isInterrupted()) {
		//	break;
		//}
		// 검사방법2) ==> Thread의 정적메서드인 interrupted()메서드를 이용하여 검사하기
		// ==> interrupt()메서드가 호출되면 true를 반환한다.
		if(Thread.interrupted()) {
			break;
		}
	}
	
	
	System.out.println("자원정리 중...");
	System.out.println("쓰레드 종료...");
	
}

}

// 쓰레드에서 객체를 공통으로 사용하는 예제

/*
원주율(PI)를 계산하는 쓰레드와 계산된 원주율을 출력하는 쓰레드가 있다.
원주율을 저장하는 객체가 필요하다.
이 객체를 두 쓰레드가 공통으로 사용해서 처리한다.

*/
public class ThreadTest15 {
public static void main(String[] args) {
//공통으로 사용할 객체 생성
ShareData sd = new ShareData();

	//쓰레드 객체를 생성하고 공통으로 사용할 객체를 쓰레드에 주입한다.
	CalcPIThread ct = new CalcPIThread();
	ct.setSd(sd);
	
	PrintPIThread pt = new PrintPIThread(sd);
	
	ct.start();
	pt.start();

}

}

// 원주율을 관리하는 클래스 작성(공통으로 사용할 클래스)

class ShareData{
    public double result; // 계산된 원주율이 저장될 수 있는 변수 선언

public boolean isOk = false; // 계산이 완료되었는지를 나타내는 변수

}

// 원주율을 계산하는 쓰레드

class CalcPIThread extends Thread{
    private ShareData sd;

//setter
public void setSd(ShareData sd) {
	this.sd = sd;
}

@Override
public void run() {
	/*
	 원주율 = (1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 ....) * 4;
	 		1 - 3 + 5 - 7 + 9 - 11
	        0   1   2   3   4   5   ==> 2로나눈 몫이 짝수일때는 +, 홀수일때 -		
	*/
	double sum = 0.0;
	for(int i = 1; i <= 100000000; i+=2) {
		if( (i/2) % 2 == 0 ) { //몫이 짝수인 경우
			sum += (1.0/i);
		}else {
			sum -= (1.0/i);
		}
	}
	
	sd.result = sum * 4; // 계산이 완료된 값을 공통객체에 저장한다.
	sd.isOk = true;
}

}

// 계산이 완료되면 계산된 원주율을 출력하는 쓰레드

class PrintPIThread extends Thread{
    private ShareData sd;

//생성자
public PrintPIThread(ShareData sd) {
	this.sd = sd;
}

@Override
public void run() {
	while(true) {
		if(sd.isOk) {
			break;
		}
		Thread.yield();
	}
	System.out.println();
	System.out.println("결과 : " + sd.result);
	System.out.println("PI : " + Math.PI);
}

}

public class ThreadTest16 {

public static void main(String[] args) {
	ShareObject sObj = new ShareObject();
	
	TestThread th1 = new TestThread("1번 쓰레드", sObj);
	TestThread th2 = new TestThread("2번 쓰레드", sObj);
	
	th1.start();
	th2.start();
}

}

//공통으로 사용할 클래스
class ShareObject{
private int sum = 0;

// 동기화 처리하기

// public synchronized void add() { //방법1 ==> 메서드에 동기화 설정하기
public void add() {

	synchronized (this) { //방법2 ==> 동기화 블럭으로 설정하기
		int n = sum; //쓰레드가 제어를 실행할 때 과정을 보려고 풀어서 쓴 것
		
		n += 10;
		
		sum = n;
		
		System.out.println(Thread.currentThread().getName() + " 합계 : " + sum);
		
	}
}

}

class TestThread extends Thread{
private ShareObject sObj;

public TestThread(String name, ShareObject sObj) {
	super(name); //쓰레드의 이름 설정
	this.sObj = sObj;
}

@Override
public void run() {
	for(int i = 1; i <= 10; i++) {
		sObj.add();
	}
}

}

//은행의 입출금을 쓰레드로 처리하는 예제(동기화 처리 예제)

public class ThreadTest17 {
private int balance; // 잔액이 저장될 변수

public int getBalance() {
	return balance;
}

public void setBalance(int balance) {
	this.balance = balance;
}

// 입금하는 메서드
public void deposite(int money) {
	balance += money;
}

// 출금하는 메서드 (출금 성공: true, 출금 실패: false 반환)

// public synchronized boolean withdraw(int money) {
public boolean withdraw(int money) {
synchronized (this) {

		if(balance >= money) {
			for(int i = 1; i <= 100000000; i++) {} //시간 지연용
			balance -= money;
			System.out.println("메서드 안에서 balance = " + balance);
			return true;
		}else {
			return false;
		}
	}
	
}

public static void main(String[] args) {
	final ThreadTest17 acount = new ThreadTest17();
	acount.setBalance(10000); //잔액을 10000원으로 설정한다.
	
	//익명 구현체로 쓰레드 만들기
	Runnable test = new Runnable() {
		
		@Override
		public void run() {
			boolean result = acount.withdraw(6000); //6000원 출금하기
			
			System.out.println("쓰레드에서 result = " + result + ", balance = " + acount.getBalance());
			
		}
	};
	//--------------------------------------------------
	
	Thread th1 = new Thread(test);
	Thread th2 = new Thread(test);
	
	th1.start();
	th2.start();
}

}

Vector와 List예제

Vector, Hashtable 등 예전부터 존재하던 Collection 객체들은 내부에 동기화 처리가 되어있다.
그런데, 최근에 새로 구성된 Collection 객체들은 동기화 처리가 되어있지 않다.
그래서 동기화가 필요한 프로그램에서 이런 Collection들을 사용하려면 동기화 처리를 한 후에 사용해야 한다.

public class ThreadTest18 {
private static Vector vec = new Vector<>();

//동기화 처리가 되지 않은 List
private static ArrayList<Integer> list1 = new ArrayList<>();

//동기화 처리를 한 List
private static List<Integer> list2 = Collections.synchronizedList(new ArrayList<Integer>());

public static void main(String[] args) {
	//익명 구현체로 쓰레드 구현
	Runnable r = new Runnable() {
		@Override
		public void run() {
			for(int i = 0; i < 10000; i++) {
				//vec.add(i);
				//list1.add(i);
				list2.add(i);
			}
		}
	};
	//---------------------------------------------
	Thread[] thArr = new Thread[] {
			new Thread(r), new Thread(r), new Thread(r),
			new Thread(r), new Thread(r)
	};
	
	for(Thread th : thArr) {
		th.start();
	}
	
	for(Thread th : thArr) {
		try {
			th.join();
		} catch (InterruptedException e) {
			// TODO: handle exception
		}
	}
	

// System.out.println("vec 개수 : " + vec.size());
// System.out.println("list1 개수 : " + list1.size());
System.out.println("list2 개수 : " + list2.size());
}

}

0개의 댓글