출처: https://bumcrush.tistory.com/182 [맑음때때로 여름]

쓰레드


– 자바에서 스레드를 사용하는 이유는 비동기적 작동 방식(독립스레드)에 대한 개념이 없음
 • 프로그래밍 방식이 자바에서는 사용이 가능하지 않음
 • 비동기적 작동 방식 구현 : ‘스레딩 기술의 사용 방법’ 숙지
– 자바가 실행되는 기반인 자바가상머신(Java virtual machine, 이하 JVM) 자체가 하나의 프로세스임
 • 언어적 차원 : 스레드의 동기화 지원 가능
 • 다중 스레드 프로그램을 쉽고 명료하게 만들 수 있음

 

다중 쓰레드

– 시분할 기법(Time-Shared)
• 멀티태스킹 및 멀티스레드를 가능하게 하는 기법
• 아주 짧은 시간 간격을 두고 여러 개의 프로그램을 전환하면서 실행
• 빠른 속도 때문에 두 개 이상의 프로그램이 동시에 실행되는 것처럼 느껴짐
• 프로그램의 실행을 전환하는 것은 OS가 담당함


– 다중 스레드의 이점
• 자원을 효율적으로 사용할 수 있음
• 사용자에 대한 응답성이 향상됨
• 작업이 분리되어 코드가 간결함


– 다중 스레드의 단점
• 동기화에 주의해야 함
• 교착상태가 발생하지 않도록 주의해야 함
• 각 스레드가 효율적으로 고르게 실행될 수 있게 해야 함

 

–다중 스레드의 사용 예

• 스마트폰 게임
 – 메인스레드 : 게임을 하기 위한 UI부분을 그려줌
 – 그래픽 부분 담당 코드 : 순차적으로 실행, UI를 그리는 서버통신을 담당하는 소켓부분을 방치하는 수 밖에 없음.
 – 통신을 담당하는 스레드를 따로 하나를 두어 일정한 시간단위로 체크할 수 있도록 해야 함.


• 영상통신

– 영상을 받아 화면에 출력해 주는 코드와 영상을 생성하여 보내주는 코드를 만드는 경우 

   > 적어도 2개의 작업이 동시에 일어난다는 것을 알 수 있음
– 두 가지 이상의 일을 구현하기 위해 다중 스레드를 사용함

 

 


< 스레드 사용 방법 >

예제 extends Thread

package thread;

// 쓰레드 클래스의 정의는 thread클래스를 상속해서 정의
public class showThread extends Thread {

	String threadName;
	
	public showThread(String name) {
		this.threadName=name;
	}
	
	@Override
	public void run() {
		
		for(int i=0; i<100; i++) {
			System.out.println("안녕하세요" + threadName + "입니다.");
			try {
				// object클래스의 sleep() 메서드 : 현재 스레드를 1/1000초 간격으로 멈춤
				sleep(100);// 100/1000초 > 1/10초 

			} catch (InterruptedException e) {
				e.printStackTrace();
			} 
			
		} System.out.println(threadName + " 종료 ★");
	}

}
package thread;

public class threadMain {

	public static void main(String[] args) {

		
		// Thread 생성
		
		showThread st1 =new showThread("1번 스레드");
		showThread st2 =new showThread("2번 스레드");
		
		st1.start();
		st2.start();
	
		
		for(int i=0; i<100; i++) {
			System.out.println("메인스레드");
		}
		
		System.out.println("★끝★");
	}

}

< 출력 >

main스레드에 Thread.sleep(100)을 똑같이 넣어주면 셋이 번갈아서 실행됨

 


예제 implements Runnable

package thread;

// 쓰레드 클래스 정의할 때 상속이 필요한 경우 Runnable 인터페이스를 구현해서 스레드를 생성할 수 있다.
public class ShowRunnable implements Runnable {

	
	public void run() {
		for(int i=0; i<100; i++) {
			System.out.println("러너블스레드");
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}
		
		System.out.println("★끝★");
	}

}
package thread;

public class threadMain2 {

	public static void main(String[] args) {

				
		// Runnable 인터페이스를 이용한 스레드
		Runnable target = new ShowRunnable();
		Thread st3 = new Thread(target);
		
		st3.start();
		
	
		
		for(int i=0; i<100; i++) {
			System.out.println("메인스레드");
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}
		
		System.out.println("★끝★");
	}

}

<출력 / 100개 반복>


스레드 우선순위 설정

		// 우선순위 설정 1-10 : 기본5
		st1.setPriority(Thread.MAX_PRIORITY);
		st2.setPriority(Thread.MIN_PRIORITY);

<출력> ~ 속도가 빨라서 별차이가 없지만 방법은 ARADUZA ~


t1.join(); // t1 스레드가 종료될때까지 다른 스레드는 멈춘상태

예제)

package thread;

public class Sum {
	
	int num;
	
	Sum(){
		num=0;
	}
	
	void addNum(int n) {
		num+=n;
	}
	
	int getNum() {
		return num;
	}

	public void run() {
		// TODO Auto-generated method stub
		
	}

}
package thread;

public class Adderthread extends Sum implements Runnable  {
	
	int start;
	int end;
	
	Adderthread(int n1, int n2){
		start=n1;
		end=n2;
	}

	@Override
	public void run() {
		for(int i=start; i<=end;i++) {
			addNum(i);
		}
	}
}
package thread;

public class AdderThreadMain {

	public static void main(String[] args) {
		
		Adderthread at1 = new Adderthread(1, 500000);
		Adderthread at2 = new Adderthread(500000, 1000000);
		
		// 쓰래드 생성
		Thread t1 = new Thread(at1);
		Thread t2 = new Thread(at2);
		
		t1.start();
		t2.start();
		
		try {
			t1.join(); // t1 쓰래드가 종료될 때 까지 다른 쓰래드는 멈춤상태
			t2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("1~100 합은 : " + (at1.getNum()+at2.getNum()) );
		

	}

}

<출력>

1~100 합은 : 1784793664


멀티스레드 예제

package thread;

import javax.swing.JOptionPane;

public class ThreadTestMain1 {

	// 다른 스레드에서도 참조가 가능한 클래스 변수 
	public static boolean check = false; // 10초 지나면 꺼지게하려고 만듦 (안에서사용하려고)
	
	public static void main(String[] args) {

//		String age= JOptionPane.showInputDialog("나이를 입력해주세요");		
//		int ageNumber=Integer.parseInt(age);		
//		System.out.println("저의 나이는 " + age + "세 입니다.");		
//		for(int i=10; i>0; i--) {
//			System.out.println(i);
//			try {
//				Thread.sleep(1000);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
//		}                       // 나이쓰는 창뜨고 > 숫자셈
		
		InputAgeThread iat= new InputAgeThread();
		CountDownThread cdt=new CountDownThread();
		
		iat.start();
		cdt.start();
	}

}

class InputAgeThread extends Thread{

	@Override
	public void run() {
		System.out.println("10초 안에 입력하세요.");
		String age= JOptionPane.showInputDialog("나이를 입력해주세요");
		System.out.println("저의 나이는 " + age + "세 입니다.");
		ThreadTestMain1.check=true;
		
	}
	

	

}

class CountDownThread extends Thread {

	@Override
	public void run() {
		for(int i=10; i>0; i--) {
			if(ThreadTestMain1.check) {
				break;                    // 입력되면 숫자세기 STOP
			}
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		System.exit(0);
	}
	
	
}

'


스레드 동기화

 

Note that this implementation is not synchronized
API 문서에는 해당 클래스의 인스턴스가 둘 이상의 쓰레드가 동시에 접근을 해도 문제가 발생하지 않는지를 명시하고 있다.  따라서 쓰레드 기반의 프로그래밍을 한다면, 특정 클래스의 사용에 앞서 쓰레드에 안전한지를 확인 해야 한다.

ex) String buffer / builder

 

쓰레드의 동기화 - synchronized
- 한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 락(lock)을 걸어서 데이터의 일관성을 유지하는 것.

 

아래 예제)

Sum 클래스의 Void addNum에  synchronized가 없으면 동기화가 되지 않아서 매번 값이 다르다..

그러나 엄청난 성능의 감소를 동반한다.

package thread;

public class Sum {
	
	int num;
	
	Sum(){
		num=0;
	}
	
	// 동기화~!
	synchronized void addNum(int n) {
		num+=n;
	}
	
	int getNum() {
		return num;
	}

	public void run() {
		// TODO Auto-generated method stub
		
	}

}
package thread;

public class AdderThread1 extends Thread {

	Sum sum;
	int start;
	int end;

	public AdderThread1(Sum sum, int start, int end) {
		this.sum = sum;
		this.start = start;
		this.end = end;
	}

	@Override
	public void run() {
		for (int i = start; i <= end; i++) {
			sum.addNum(i);
		}

	}

}
package thread;

public class AdderThreadhMain2 {

	public static void main(String[] args) {

		Sum sum = new Sum();
		AdderThread1 t1 = new AdderThread1(sum, 1, 5000);
		AdderThread1 t2 = new AdderThread1(sum, 5001, 10000);
		
		t1.start();
		t2.start();
		
		try {
			t1.join();
			t2.join();			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println("1-100합 "+ sum.getNum());
	}

}

 

<출력>

1-100합 50005000

 

동기화블럭

public class Calculator {

	int opplCnt=0; // 몇번의 연산을 했나요?
	int opmiCnt=0;
	
	
	public int add(int n1, int n2) {
		synchronized (this) { // key
			opplCnt++;			
		}
//		opCnt++;	
		return n1+n2;
	}
	
	
	public int min(int n1, int n2) {
		synchronized (obj) { // 참조값이 달라서 다르다.
			opmiCnt++;
		}
//		opCnt++;
		return n1-n2;
	}
	
	Object obj = new Object(); // 새로운 동기화 키 생성
}

만약 opplCnt / opmiCnt가 아니라 똑같이 opCnt++;를 사용하면 똑같이 this(둘다같은거면OK)를 넣으면 된다

 

num1도 쓰고 num2도 쓰기 위해(효율성) 두개의 Key로 분리해 주는 것이라고 생각하면 되는듯하다.

 

+ Recent posts