Day30-31. Thread(쓰레드) (3) Synchronized
먼저
쓰레드 사용법
1. class 사용
-> 쓰레드를 사용하는 클래스에 Thread를 상속한다.
class A extends Thread{
public void run(){
}
}
=> 객체 만들고 start() 하면 됩니다.
A a = new A();
a.start(); -> run 메드 실행
2. interface 구현
-> 쓰레드 사용을 위해 Runnable 이라는 인터페이스를 구현한다.
class B implements Runnable{
public void run(){
}
}
=> Thread를 따로 만들어서 start() 해야 합니다.
B b = new B();
(b.start(); => 안 됨!)
Thread temp = new Thread(b); // 그래서 쓰레드를 따로 만들어줘야 함.
temp.start();
쓰레드 동기화
- 여러개의 쓰레드를 동작시킬 때면, 이것의 동작을 제어도 해줘야 하고, 접근하게 만들려면 동기화를 시켜줘야 한다.
이 때 나오는 개념이
임계영역
다수의 쓰레드가 하나의 쓰레드에 접근하려고 할 때, 그 영역
==> 작업 순간마다 한 개의 쓰레드만 사용할 수 있게 하는 영역
아까 말했던 예시처럼.
식당에서 요리를 하고 나서 서빙을 하는 것처럼.
요리 할 때는 서빙하는 사람이 방해를 하면 안 되죠.
이걸 만들 수 있는 메서드 키워드가
synchronized
=> 이게 없으면 난리 나버립니다.
int a = 5; 라고 할 때
- 증가 쓰레드가 먼저 동작에 들어가서 5를 가져와서 6으로 증가를 시킴.
- 안정적으로 6이 되어야 하는데 / 그 순간에 synchronized를 하지 않으면 감소 쓰레드가 들어와서 도로 5를 만들어버린다.
(원래 의도는 5 -> 6(먼저 증가쓰레드) -> 5(다시 감소쓰레드, 차례대로 동작하게 시키고 싶었음)
- 그런데 감소 쓰레드가 가져올 때 순간 타이밍에 a가 5였다면, 아예 4를 만들어버리는 경우도 빈번하게 발생할 수 있음.
* 임계영역이 작동하면, 감소 쓰레드가 접근을 못 하고 있다가,
증가 쓰레드가 다 끝나고 6이라는 결과를 만들어 내고 나면, 감소쓰레드가 접근해서 그 6을 다시 5로 만든다.
ex. 공중화장실=> 한 번에 한 명만 문 잠그고 들어가서 사용. 만약 문 부수고 들어가면? 범죄자!
=> 데이터의 유효성 보장!!!!
하지만
주의사항
=> 한 쓰레드만 들어가는 것을 보장하는 것이지, 순서를 보장하는 건 아니다!!!
예시코드
package Day30;
class A {
synchronized void plus(int i) throws InterruptedException {
// 나에게 접근하고 싶으면 한 번에 하나씩 들어와!
for (int j = 0; j<5; j++) {
System.out.println(j*i);
Thread.sleep(800);
// 얘 명령이 0.8초를 자기 때문에 원래는 다른 객체가 쏜쌀같이 들어오는데,
// 이걸 싱크로나이즈드 붙여서 막는다.
}
}
}
class B extends Thread {
A a; // has 관계
int i;
B(A a, int i) {
this.a = a;
this.i = i;
}
public void run() {
try {
a.plus(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class EXThread2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 객체는 하나로 돌려막기 해야함. // 객체를 동시에 똑같은 것 쓰게 만들어야.
A a1 = new A();
B b1 = new B(a1, 3); // 3이 먼저 시작한 건 맞으나, 꼭 3이 먼저 들어가는 건 아니다.
B b2 = new B(a1, 7); // 만약에 CPU가 3을 뒤로 미뤄놨다면, 7이 들어갈 수도 있음(찰나의 상황)
b1.start();
b2.start();
}
}
실행결과
0
3
6
9
12
0
7
14
21
28
// 차례대로 들어온다!
만약 synchronized가 없다면?
package Day30;
class A {
void plus(int i) throws InterruptedException {
// 키워드 없음
for (int j = 0; j<5; j++) {
System.out.println(j*i);
Thread.sleep(800);
}
}
}
class B extends Thread {
A a; // has 관계
int i;
B(A a, int i) {
this.a = a;
this.i = i;
}
public void run() {
try {
a.plus(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class EXThread2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 객체는 하나로 돌려막기 해야함. // 객체를 동시에 똑같은 것 쓰게 만들어야.
A a1 = new A();
B b1 = new B(a1, 3);
B b2 = new B(a1, 7);
b1.start();
b2.start();
}
}
실행 결과 => 순서 중구난방
0
0
3
7
6
14
9
21
28
12