쓰레드
– 자바에서 스레드를 사용하는 이유는 비동기적 작동 방식(독립스레드)에 대한 개념이 없음
• 프로그래밍 방식이 자바에서는 사용이 가능하지 않음
• 비동기적 작동 방식 구현 : ‘스레딩 기술의 사용 방법’ 숙지
– 자바가 실행되는 기반인 자바가상머신(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로 분리해 주는 것이라고 생각하면 되는듯하다.
'JAVA > basic' 카테고리의 다른 글
[JDBC] 자바와 데이터 베이스(Oracle SQL) 연결하기 (0) | 2020.11.18 |
---|---|
[ 네트워크 ] 소켓 / Socket 클래스 (0) | 2020.10.28 |
[ 네트워크 ] URL (0) | 2020.10.28 |
[ 입출력 ] 버퍼 스트림 (0) | 2020.10.26 |
[ 입출력 ] File (0) | 2020.10.26 |
[ 입출력 ] 직렬화 (Serializable) (0) | 2020.10.26 |
[ 입출력 ] 문자스트림 : BufferedWriter / BufferedReader / PrintWriter (0) | 2020.10.26 |