Thread(스레드)

서현서현·2022년 4월 14일
0

JAVA

목록 보기
15/27
post-thumbnail

Thread

  • process(운영체제에서 실행중인 하나의 프로그램) 내에서 실행되는 세부 작업 단위
  • 하나의 프로세스 내에 여러개의 스레드가 존재 할 수 있다.
  • 빠르다

싱글스레드

// 싱글스레드 : 스레드가 하나뿐인 프로그램

public class T01_TreadTest {
	public static void main(String[] args) {
		for(int i =1; i<=200; i++) {
			System.out.print("*");
		}
		
		System.out.println();
		
		for(int i =1; i<=200; i++) {
			System.out.print("$");
		}
		
	}

}

스레드가 하나이므로 순차적으로 실행된다.

멀티스레드

멀티스레드를 만드는 세가지 방법!

// 멀티스레드 : 스레드가 2개이상

public class T02_TreadTest {
	public static void main(String[] args) {
		//방법1 : Thread클래스를 상속한 class의 인스턴스를 생성한 후 이 인스턴스의 start()메소드를 호출한다.
		MyThread1 th1 = new MyThread1();
		th1.start();
		
		//방법2 : Runnable 인터페이스를 구현한 class의 인스턴스를 생성한 후 
		//이 인스턴스를 Thread객체의 인수턴스를 생성할때 생성자의 매개변수로 넘겨준다.
		//이때 생성된 Thread객체의 인스턴스의 start()메소드를 호출한다
		// void run()을 Runnable이라는 인터페이스로 구현한것이다
		Runnable r = new MyThread2();
		Thread th2 = new Thread(r);
		th2.start();
		
		//방법3 : 익명클래스를 이용하는 방법 : 재사용 할 필요 없을때
		//Runnable 인터페이스를 구현한 익명클래스를 Tread인스턴스를 생성할때 매개변수로 넘겨준다
		Thread th3 = new Thread(new Runnable() {

			@Override
			public void run() {
						for(int i =1; i<=200; i++) {
							System.out.print("@");
							
							try {
								// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춘다
								// 시간은 밀리세컨드 단위를 사용한다
								// 즉 1000=1초
								Thread.sleep(100);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						
						}
				
			}
			
		});
	
		th3.run();
	}

}

class MyThread1 extends Thread{
	@Override
	public void run() {
		for(int i =1; i<=200; i++) {
			System.out.print("*");
			
			try {
				// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춘다
				// 시간은 밀리세컨드 단위를 사용한다
				// 즉 1000=1초
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class MyThread2 implements Runnable{
	public void run() {
		
		for(int i =1; i<=200; i++) {
			System.out.print("$");
			
			try {
				// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춘다
				// 시간은 밀리세컨드 단위를 사용한다
				// 즉 1000=1초
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		
		}
	}
}

프로그램 종료시점: 메인스레드를 포함한 모든 스레드가 모두 종료된 경우!

클래스는 다중상속이 안되기 때문에 방법1을 사용할 경우 이미 상속된 객체가 있다면 스레드로 돌리지 못한다는 단점이 있다. → 이런경우 방법2를 써서 Runnable을 implement하면 됨!

상태설명
NEW스레드가 생성되고 start()가 호출되지 않은 상태
RUNNABLE실행중 또는 실행 가능한 상태 ( start()호출한 상태 / 다른애가 CPU반납하길 기다리는 상태 )
BLOCKED일시정지 된 상태
WAITING, TIMED_WAITING스레드 작업 종료되진 않았지만 실행가능하지 않은 일시정지 상태, TIMED_WAITING은 일시정지시간이 지정된경우
TERMINATED스레드의 작업이 종료된 상태

(예제) 스레드의 수행시간 체크해보기

public class T03_ThreadTest {
	
	public static void main(String[] args) {
		
		// 스레드의 수행시간 체크해보기
		
		Thread th = new Thread(new MyRunner());
		
		/*
		 	UTC(Universal Time Coordinated 협정세계표준시)를 사용하여 1970년 1월1일
		 	0시0분0초를 기준으로 경과한 시간을 밀리세컨드 단위로 나타낸다
		*/

		long startTime = System.currentTimeMillis();
		
		th.start(); // 스레드 작업 시작
		
		try {
			th.join(); // 현재 실행중인 스레드에서 작업중인 스레드(지금은th스레드)가 종료될때까지 기다린다
		} catch (InterruptedException e) {
			// TODO: handle exception
		}
		
		long endTime = System.currentTimeMillis();
		
		System.out.println("경과시간: "+(endTime-startTime));
	}

}

// 1~1000000000 까지의 합계를 구하는 스레드 클래스
class MyRunner implements Runnable{

	@Override
	public void run() {
		long sum = 0;
		for(int i =0; i<=1000000000; i++) {
			sum += i;
		}
		System.out.println("합계: "+sum);
	}
	
}

(예제2) 합계를 구하는데 걸린시간 체크하기

public class T04_ThreadTest {
	/*
	 	1~20 억까지의 합계를 구하는데 걸린 시간 체크하기
	 	전체 합계를 구하는 작업을 단독으로 했을 때 (1개의 스레드를 사용했을때)와 
	 	여러 스레드로 분할해서 작업할때의 시간을 확인해보자.
	*/
	
	
	public static void main(String[] args) {
		
		
		// 단독으로 처리했을때
		SumThread sm = new SumThread(1L, 2000000000L);
		
		long startTime = System.currentTimeMillis();
		
		sm.start();
		
		try {
			sm.join();
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		
		long endTime = System.currentTimeMillis();
		
		System.out.println("단독으로 처리할떄의 처리시간: "+(endTime-startTime));
		System.out.println("\n\n");
		
		
		
		
		//여러 스레드가 협력해서 처리할때
		SumThread[] sumThs = new SumThread[] {
			new SumThread(1L,           500000000L),
			new SumThread(500000001L,  1000000000L),
			new SumThread(1000000001L, 1500000000L),
			new SumThread(1500000001L, 2000000000L)
		};
		
		startTime = System.currentTimeMillis();
		
		for(int i =0; i<sumThs.length;i++) {
			sumThs[i].start();
		}
		

		for(SumThread th : sumThs) {
			try {
				th.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
			
		endTime = System.currentTimeMillis();
			
		System.out.println("협력해서 처리 했을때의 시간: "+(endTime-startTime));

			
	}

}

class SumThread extends Thread {
	private long max,min;
	
	public SumThread(long min, long max) {
		this.min = min;
		this.max=max;
	}
	
	@Override
	public void run() {
		long sum = 0L;
		for(long i=min; i<=max; i++) {
			sum+=i;
		}
		System.out.println(min+" ~ "+max+" 까지의 합 : "+sum);
	}
}

(예제3) 10초안에 입력받기

import javax.swing.JOptionPane;

public class T05_ThreadTest {
	public static void main(String[] args) {
		String str = JOptionPane.showInputDialog("아무거나 입력하세요: ");
		System.out.println("입력값은 "+str+"입니다.");
		
		for(int i =10; i>=1; i--) {
			System.out.println(i);
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

한 스레드에서 실행하면 동시 실행이 불가능하므로 입력을 끝낸 후 10초를 세게 된다.

(예제4) 10초안에 입력받기

import javax.swing.JOptionPane;

public class T06_ThreadTest {
	
	public static boolean inputChk;

	public static void main(String[] args) {
				
		Thread th1 = new DataInput();
		Thread th2 = new CountDown();
		
		th1.start();
		th2.start();
	}

}

class DataInput extends Thread {
	@Override
	public void run() {
		String str = JOptionPane.showInputDialog("아무거나 입력하세요");
		//입력이 완료되면 true
		T06_ThreadTest.inputChk = true;
		System.out.println("입력한 값은 "+str+"입니다");
	}
}

class CountDown extends Thread {
	@Override
	public void run() {
		
		for(int i=10; i>=1; i--) {
			System.out.println(i);
			
			// 입력이 완료되었는지 여부를 검사하고 입력이 완료되면 run()을 종료시킨다. 즉 현재 스레드 종료
			
			if(T06_ThreadTest.inputChk) {
				return; //true면 for문 돌 필요 없으니까 return; 으로 근접한 그 메소드 빠져나감 (break는 for문탈출)
			}
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		//10초가 경과되었는데도 입력이 없으면 프로그램 종료
		System.out.println("10초가 지났습니다. 프로그램을 종료합니다");
		System.exit(0);
	}
}


Z

두 스레드가 동시에 동작하므로 10초 카운트다운을 하다 입력이 들어오면 카운트다운을 멈춘다.

Priority

  • 우선순위는 1 ~ 10 까지 부여된다. 1이 가장 우선순위가 낮고, 10이 가장 높다. 기본적으로 우선순위 5를 할당받고 변경하고 싶으면 setPriority() 메소드를 이용한다.
public class T08_ThreadPriorityTest {
	
	public static void main(String[] args) {
		System.out.println(" 최대 우선순위 : "+ Thread.MAX_PRIORITY);
		System.out.println(" 최소 우선순위 : "+ Thread.MIN_PRIORITY);
		System.out.println(" 보통 우선순위 : "+ Thread.NORM_PRIORITY);
		
		
		Thread[] ths = new Thread[] {
				new ThreadTest1(),
				new ThreadTest1(),
				new ThreadTest1(),
				new ThreadTest1(),
				new ThreadTest1(),
				new ThreadTest2()
		};
		
		
		// 우선순위는 start()를 호출하기 전에 설정해야한다
		for(int i=0; i<ths.length; i++) {
			if(i==5) {
				ths[i].setPriority(10);
			}else {
				ths[i].setPriority(1);				
			}
		}
		
		// 우선순위 출력
		for(Thread th : ths) {
			System.out.println(th.getName()+"의 우선순위 : "+th.getPriority());
		}
		
		for(Thread th : ths) {
			th.start();
		}
		
		
	}

}

// 대문자를 출력하는 스레드

class ThreadTest1 extends Thread {
	@Override
	public void run() {
		for(char ch='A'; ch<='Z'; ch++) {
			System.out.println(ch);
			
			// 아무것도 하지 않는 반복문 (시간때우기용)
			for (long i=1; i<=1000000000; i++) {}
		}
	}
}

// 소문자를 출력하는 스레드

class ThreadTest2 extends Thread {
	@Override
	public void run() {
		for(char ch='a'; ch<='z'; ch++) {
			System.out.println(ch);
			
			// 아무것도 하지 않는 반복문 (시간때우기용)
			for (long i=1; i<=1000000000; i++) {}
		}
	}
}

Demon Thread

  • 주 스레드 역할을 돕는 보조적인 역할
  • 주 스레드 종료시 데몬스레드 강제종료
// 스레드를 데몬스레드로 설정하기

public class T09_ThreadDaemonTest {
	public static void main(String[] args) {
		Thread th = new AutoSaveThread();

		// 데몬스레드로 설정하기 (start()를 호출하기 전에 설정해야 한다)
		th.setDaemon(true);
		th.start();
		
		try {
			for(int i = 1; i<=20; i++) {
				System.out.println("작업 - "+i);
				Thread.sleep(1000);
			}
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		System.out.println("메인스레드 종료...");
	}

}

// 자동저장 기능을 제공하는 스레드 (3초에 한번씩 저장)

class AutoSaveThread extends Thread {
	
	@Override
	public void run() {
		while(true) {
			
			try {
				Thread.sleep(3000);
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			save(); //저장기능호츌
		}
		
	}
	
	public void save() {
		System.out.println("작업 내용을 저장합니다...");
	}
	
}

Thread State

get State(). 처음엔 NEW상태이고 start()할려고 만들어놓은거임

start()하면 Runnable 됨

실행하고있는거, 실행할순 없지만 나에게 CPU할당될때까지 기다리는상태 보두 Runnable

Sleep : 재우면 Runnable에서 Waiting으로 바뀜(시간 부여하면 TimedWaiting) 깨어나면 다시 Runnable

쓰레드의run()이 끝나면 종료상태 즉 Terminate 상태가 된다

// 스레드의 상태를 확인하기

public class T10_ThreadStateTest {
	public static void main(String[] args) {
	
		Thread th = new StatePrintThread(new TargetThread());
		th.start();
	}
}

// 스레드의 상태를 출력하는 클래스
class StatePrintThread extends Thread {
	private Thread targetThread; //모니터링 할 스레드 저장용
	
	public StatePrintThread(Thread targetThread) {
		this.targetThread = targetThread;
	}
	
	@Override
	public void run() {
		while(true) {
			// Thread의 상태 구하기(getState()이용)
			Thread.State state = targetThread.getState();
			System.out.println("타겟스레드의 상태값: "+state.name());
			
			// NEW 상태인지 검사
			if(state == Thread.State.NEW) {
				targetThread.start();
			}
			
			// 타겟스레드가 종료상태인지 검사
			if(state == Thread.State.TERMINATED) {
				break;
			}
			
			try {
				Thread.sleep(500);
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}
	}
}

// 모니터링 대상 스레드 클래스
class TargetThread extends Thread {
	@Override
	public void run() {
		for(long i=1; i<= 1000000000L; i++) {} //시간 지연용
		try {
			Thread.sleep(1500);
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		for(long i = 1; i<=1000000000L; i++) {} //시간지연용
	}
}

(예제)

// 3개(명)의 스레드가 각각 알파벳 대문자를 출력하는데 출력을 끝낸 순서대로 결과를 나타내는 프로그램 작성하기
public class T11_DisplayCharacterTest {
	
	static String strRank = "";
	
	public static void main(String[] args) {
		
		Thread[] ths = new Thread[] {
			new DisplayCharacter("홍길동"),	
			new DisplayCharacter("일지매"),	
			new DisplayCharacter("변학도")	
		};
		
		for(Thread th : ths) {
			th.start();
		}
		
		for(Thread th : ths) {
			try {
				th.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("경기 끝...");
		System.out.println("------------------------");
		System.out.println();
		System.out.println(" 경기 결과 ");
		System.out.println("순위 : " + strRank);
		
	}
}

// 영어 대문자를 출력하는 스레드 클래스
class DisplayCharacter extends Thread {
	private String name;
	
	public DisplayCharacter(String name) {
		this.name = name;
	}
	
	@Override
	public void run() {
		for(char ch='A'; ch<='Z'; ch++) {
			System.out.println(name + "의 출력 문자 : " + ch);
			
			try {
				// sleep()의  값을 200~500사이의 난수로 한다.
				Thread.sleep((int)(Math.random() * 301 + 200));
			}catch(InterruptedException ex) {
				ex.printStackTrace();
			}
		}
		System.out.println(name + " 출력 끝...");
		
		T11_DisplayCharacterTest.strRank += name + " ";
	}
}

Thread yield()

  • 현재 실행 대기중인 동등한 우선순위 이상의 다른 스레드에게 실행기회를 제공한다.(양보)
  • 현재 실행중인 스레드의 상태를 Runnable 상태로 바꾼다. (Waiting이나 Blocked 상태로 바뀌지 않는다.)
  • yield() 메서드를 실행한다고 해서 현재 실행 중인 스레드가 곧바로 Runnable상태로 전이된다고
    확신할 수 없다.
public class T12_ThreadYieldTest {
	public static void main(String[] args) {
		Thread th1 = new YieldThread1();
		Thread th2 = new YieldThread2();
		
		th1.start();
		th2.start();
	}
}

class YieldThread1 extends Thread {
	public YieldThread1() {
		super("양보 스레드");
	}
	
	@Override
	public void run() {
		for(int i=0; i<10; i++) {
			System.out.println(Thread.currentThread().getName()
					+ " : " + i);
			Thread.yield(); // 양보하기
		}
	}
}
class YieldThread2 extends Thread {
	public YieldThread2() {
		super("비양보 스레드");
	}
	
	@Override
	public void run() {
		for(int i=0; i<10; i++) {
			System.out.println(Thread.currentThread().getName()
					+ " : " + i);
		}
	}
}

stop()

중지하는 메소드 : 정리 안하고 꺼버림

비추천 (deprecated)

그렇다면 그 외 다른방법 ? :

1) 상태값 만들어놓고 상태에 따라 적절히 종료시키는것(True,False로 상태값을 만들기)

2) interrupt()메소드를 이용하여 해당 스레드에 interrupt()를 건다. 적절한 액션을 취할수가 있음 sleep()이나 join() 시행시 IntteruptedException 걸릴 수 있어 try-catch로 감싸서 해당 sleep메소드 실행시점에 catch로 빠진다. 그 흐름을 이용해서 빠지고 싶은 구문을 만들어 빠져나와 종료한다.

intterupt 검사 : instance() 메소드나(인터럽트 걸렸으면 True 리턴, break로 적절하게 빠져나오면 ㅇㅋ) static()메소드를(Thread.interrupted 하면 외부에서 인터러티드 걸렸으면 true 리턴됨 빠져나와서 중지되도록 하기..) 이용 (두개 차이점은 static은 두번째 턴 부터는 다시 false로 초기화돼서 인터럽티드가 한번만 유효)

공유객체

공통으로 바라보는 객체 (=공유객체) 를 통해 주고받음

ex. 원주율정보 담은 ShareData 객체 같은거,,

Valotile

특정변수 선언시 붙여주면 해당 변수는 최적화 된 상태에서 사용됨

원본 데이터를 항상 바라보고 접근하도록 처리해주는... 때문에 속도는 느리지만 원래 데이터를 확실하게 읽어와준다.

임계영역

동기화는 왜 필요한가 ? 여럿과 공유하기 때문에 예기치 않은 결과가 발생 할 수 있기 때문에 임계영역을 동기화처리해줘야한다

1) 메소드 자체에 Syncronized 해준다. 메소드 처리중에 본 스레드 외의 스레드는 해당 공유객체에 접근 불가능하게함 (다른 스레드는 기다리고 있음 즉 Blocked(동기화할때 나오는 상태))

2) 동기화블록을 만든다(중괄호를 감싸서 Syncronized)

SyncAccountTest

// 은행의 입출금을 스레드로 처리하는 예제
//(syncronized를 이용한 동기화 처리)
public class T16_SyncAccountTest {
	public static void main(String[] args) {
		SyncAccount sAcc = new SyncAccount();
		
		sAcc.deposit(10000);
		
		BankThread bth1 = new BankThread(sAcc);
		BankThread bth2 = new BankThread(sAcc);
		
		bth1.start();
		bth2.start();
	}

}

// 은행의 입출금을 관리하는 클래스 정의
class SyncAccount{
	private int balance; //잔액
	
	public synchronized int getBalance() {
		return this.balance;
	}
	
	//입금 처리를 수행하는 메소드
	public void deposit(int money) {
		balance += money;
	}
	
	//출금을 처리하는 메서드(출금성공:true, 출금실패:false 반환)
	// 동기화 영역에서 호출하는 메서드도 동기화 처리를 해주어야 한다
	// ㄴ getBalance()도 동기화 해줘야 문제가 없단소리
	
	public boolean withdraw(int money) {
		if(balance >= money) { //잔액이 많을경우
			
			for(int i=1; i<=1000000000; i++) {} //시간 지연용
				
				balance -=money;
				
				System.out.println("메서드 안에서 balance = "+getBalance());
				
				return true;
			} else {
				return false;
			}
	}
}

// 은행업무를 처리하는 스레드
class BankThread extends Thread {
	private SyncAccount sAcc;
	
	public BankThread(SyncAccount sAcc) {
		this.sAcc = sAcc;
	}
	
	@Override
	public void run() {
		boolean result = sAcc.withdraw(6000); //6000원 인출
		System.out.println("스레드 안에서 result = "+result+", balance = "+sAcc.getBalance());
	}
}

public boolean withdraw(int money) {} 에 두개가다 들어와서 순서대로 돌기 때문에 -나옴

두개이상 스레드가 한 객체를 보고 작업하면, 이렇게 문제가 발생 할 수 있다 그리고 이 영역을 임계영역이라 하며 동기화 처리가 필요하다.

**synchronized** public boolean withdraw(int money) {
		if(balance >= money) { //잔액이 많을경우
			
			for(int i=1; i<=1000000000; i++) {} //시간 지연용
				
				balance -=money;
				
				System.out.println("메서드 안에서 balance = "+getBalance());
				
				return true;
			} else {
				return false;
			}
	}
}

Lock을 이용한 동기화처리

락 기능을 제공하는 클래스
ReentrantLock => Read 및 Write 구분 없이 사용하기 위한 Lock클래스로 동기화 처리를 위해 사용
synchronized를 이용한 동기화 처리보다 부가적인 기능이 제공됨
ex) Fairness 설정 등 => 가장 오래 기다린 스레드가 가장 먼저 락을 획득하게 함
ReetrantReadWriteLock => Read 및 Write 락을 구분하여 사용 가능함
재귀적으로 여러번 Read나 Write 락을 획득할 수 있다.(재진입 가능)
여러 스레드가 동시에 read작업은 가능하지만, write작업은 단지 하나의 스레드만 가능함(exclusive)
=> Write보다 Read 위주의 작업이 많이 발생하는 경우에 사용하면 처리량(Throughput)이 좋아진다.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// 은행의 입출금을 스레드로 처리하는 예제
// (Lock객체를 이용한 동기화 처리)

public class T17_LockAccountTest {
	

	
	public static void main(String[] args) {
		
		ReentrantLock lock = new ReentrantLock(true); // true는 fairness 설정 킨것임! //락 객체 생성
		
		LockAccount lAcc = new LockAccount(lock);
		lAcc.deposit(10000);
		
		BankThread2 bth1 = new BankThread2(lAcc);
		BankThread2 bth2 = new BankThread2(lAcc);
		
		bth1.start();
		bth2.start();
		
	}
	
}

// 입출금을 담당하는 클래스
class LockAccount{
	private int balance;
	
	//Lock 객체 생성 => 되도록이면 private final로 만든다
	private final Lock lock;
	
	public LockAccount(Lock lock) {
		this.lock = lock;
	}
	
	public int getBalance() {
		return balance;
	}
	
	// 입금하는 메서드
	public void deposit(int money) {
		//Lock 객체의 lock()메서드가 동기화의 시작이고, unlock()메서드가 동기화의 끝을 나타낸다.
		// lock()메서드로 동기화를 설정한 곳에서는 반드시 unlock()메서드로 해제해 주어야 한다.
		lock.lock(); // 동기화 시작 (락을 획득하기 전까지 BLOCKED 됨)
		balance += money; // 동기화 처리부분
		lock.unlock(); // 동기화 끝
	}
	
	// 출금하는 메서드(출금성공 : True, 출금실패 : False)
	public boolean withdraw(int money) {
		boolean chk = false;
		
		if(lock.tryLock()) { // 락 획득 시도( 성공시 true, 실패시 false)
			System.out.println(Thread.currentThread().getName() + " : 락 획득 성공!");
			
			
			//try~catch 블럭을 사용할 경우에는 unlock() 호출은 finally블럭에서 하도록 한다.
			try {
				if(balance >= money) {
					for(int i=1; i<=1000000000; i++) {} //시간지연용
					balance -= money;
					System.out.println("메서드 안에서 balance = "+getBalance());
					chk = true;
				}
			} catch (Exception ex) {
				ex.printStackTrace();
			}finally{
				lock.unlock(); //락 해제(동기화 끝)
				System.out.println("락 해제 (반납) 완료..");
			}	
		}else { //락 획득 실패시..
			System.out.println(Thread.currentThread().getName()+" : 락 획득 실패!");
		}
		
		return chk;
	}
}

// 은행업무를 처리하는 스레드
class BankThread2 extends Thread {
	private LockAccount lAcc;
	
	public BankThread2(LockAccount lAcc) {
		this.lAcc = lAcc;
	}
	
	@Override
	public void run() {
		boolean result = lAcc.withdraw(6000);
		System.out.println(Thread.currentThread().getName()+" 스레드 안에서 result = "+ result+", balance = "+lAcc.getBalance());
	}
}

Collection Framework

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

Collections에서 동기화 처리하는거 제공.... ..

오류남


import java.util.ArrayList;
import java.util.List;

// 리스트 동기화 처리 예제

public class T18_SyncCollectionTest {

	
	//동기화 처리를 하지 않은 경우
	private static List<Integer> list1= new ArrayList<Integer>();
	
	public static void main(String[] args) {
		
		//익명클래스를 이용한 스레드 구현
		Runnable r = new Runnable() {
			
			@Override
			public void run() {
				for(int i = 1; i<=10000; i++){
					list1.add(i);
				}
			}
			
		};
		
		Thread[] ths = new Thread[] {
				new Thread(r), new Thread(r),
				new Thread(r), new Thread(r), new Thread(r)
		};
		
		for(Thread th : ths) {
			th.start();
		}
		
		for(Thread th : ths) {
			try {
				th.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println("list1의 개수: "+list1.size());
	}
}

동기화 블록을 써도 되고..

@Override
			public void run() {
				for(int i = 1; i<=10000; i++){
					synchronized (list1) {
						list1.add(i);	
					}
				}
			}

이렇게 할수있음.. Map도 동일한 방식으로 가능

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

// 리스트 동기화 처리 예제

public class T18_SyncCollectionTest {

	
	//동기화 처리를 하지 않은 경우
//	private static List<Integer> list1= new ArrayList<Integer>();
	
	//동기화 처리를 한 경우
	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 = 1; i<=10000; i++){
					list2.add(i);
//					list.add(i);
				}
			}
			
		};
		
		Thread[] ths = new Thread[] {
				new Thread(r), new Thread(r),
				new Thread(r), new Thread(r), new Thread(r)
		};
		
		for(Thread th : ths) {
			th.start();
		}
		
		for(Thread th : ths) {
			try {
				th.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		//System.out.println("list1의 개수: "+list1.size());
		System.out.println("list2의 개수: "+list2.size());
	}
}

Wait와 Notify

  • 동기화영역내에서만 의미가 있다.
  • 특정 스레드가 진입했을때 특별히 할 일 없다면 lock 풀고 기다리는게 낫다 = wait()
  • 이제 이걸 깨워주는게 notify()

0개의 댓글