공유 객체를 사용할 때의 주의할 점
멀티 스레드 프로그램에서 스레드들이 객체를 공유해서 작업해야 하는 경우, 스레드 A가 사용하던 객체를 스레드 B가 상태를 변경할 수 있기 때문에 스레드 A가 의도한 것과는 다른 결과가 나올 수 있다.
혼공자 책의 그림을 보고 이해해보자.
User1 스레드가 Calculator 객체의 momory필드에 100을 먼저 저장
2초간 일시정지
그 동안 User2스레드가 memory필드에 50을 저장
2초간 일시정지
2초가 지나 User1스레드가 다시 실행 되어 memory 필드 값을 출력하면, 100이 아닌 User2스레드가 저장한 50이 출력된다.
예시1) 공유 객체 사용
메인 스레드가 실행하는 코드
public class MainThreadEx {
public static void main(String[] args) {
Calculator calculator = new Calculator();
User1 user1 = new User1(); //User1 스레드 생성
user1.setCalculator(calculator);//공유 객체 설정
user1.start(); //User1 스레드 시작
User2 user2 = new User2(); //User2 스레드 생성
user2.setCalculator(calculator);//공유 객체 설정
user2.start(); //User2 스레드 시작
}
}
공유 객체
public class Calculator {
private int memory;
public int getMemory() {
return memory;
}
public void setMemory(int memory) { // 계산기 메모리에 값을 저장하는 메소드
this.memory = memory; // 매개값을 memory 필드에 저장
// 스레드를 2초간 일시 정지 시킴
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + ": " + this.memory);
// Thread.currentThread().getName() --> 스레드 이름 얻기
// this.memory --> 메모리 값
}
}
User1스레드
public class User1 extends Thread {
private Calculator calculator;
public void setCalculator(Calculator calculator) {
this.setName("User1"); // 스레드 이름을 User1로 설정
this.calculator = calculator; // 공유 객체인 Calculator를 필드에 저장
}
public void run() {
calculator.setMemory(100); // 공유 객체인 Calculator 메모리에 100을 저장
}
}
User2스레드
public class User2 extends Thread {
private Calculator calculator;
public void setCalculator(Calculator calculator) {
this.setName("User2"); // 스레드 이름을 User2로 설정
this.calculator = calculator; // 공유 객체인 Calculator를 필드에 저장
}
public void run() {
calculator.setMemory(50); // 공유 객체인 Calculator 메모리에 50을 저장
}
}
User1에 100을 저장하였으나, User1 & User2 모두 50이 나오는 것을 확인할 수 있다.
동기화 메소드
위의 예시1) 처럼 되지 않기 위해서, 사용 중인 객체를 다른 스레드가 변경할 수 없게 하려면 객체에 잠금을 걸어 다른 스레드가 사용할 수 없도록 해야한다.
멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역(critical section)이라 한다.
자바는 임계 영역을 지정하기 위해 동기화 메소드를 제공한다. 스레드가 내부의 동기화 메소드를 실행하면 다른 스레드가 동기화 메소드를 실행하지 못하도록 한다.
public synchronized void method(){
임계 영역; //단 하나의 스레드만 실행
}
동기화 메소드가 여러 개 있을 경우, 스레드가 이들 중 하나를 실행할 때 다른 스레드는 실행할 수 없다. 하지만 이때 다른 스레드에서 일반 메소드는 실행이 가능하다.
예시1) 동기화 메소드
동기화 메소드로 수정된 공유 객체
public class Calculator {
private int memory;
public int getMemory() {
return memory;
}
public synchronized void setMemory(int memory) { // 계산기 메모리에 값을 저장하는 메소드
this.memory = memory; // 매개값을 memory 필드에 저장
// 스레드를 2초간 일시 정지 시킴
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + ": " + this.memory);
// Thread.currentThread().getName() --> 스레드 이름 얻기
// this.memory --> 메모리 값
}
}
public synchronized void setMemory(int memory) --> 이렇게 수정하면,
아래처럼 User1 100 / User2 50이 나오는 것을 확인 할 수 있다.
User1 스레드가 종료 될 때 까지 User2 스레드는 실행되지 않는다.
※용어 정리
-
프로세스: 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션이 실행되는 것이다.
-
멀티 스레드: 하나의 프로세스 내에 동시 실행을 하는 스레드들이 2개 이상인 경우이다.
-
메인 스레드: 자바의 모든 애플리케이션은 메인 스레드가 main()메소드를 싱행하면서 시작한다. 메인 스레드는 main() 메소드의 첫 코드부터 아래로 순차적으로 실행하고, main()메소드의 마지막 코드를 실행하거나 return문을 만나면 실행이 종료 된다.
-
작업 스레드: 메인 작업 이외에 병렬 작업의 수만큼 생성하는 스레드를 말한다. 작업 스레드도 객체로 생성되기 때문에 크래스가 필요하다. Thread 클래스를 직접 객체화해서 생성할 수도 있고, Thread클래스를 상속해서 하위 클래스를 만들어 생성할 수도 있다.
-
동기화 메소드: 멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역 이라한다. 자바는 임계 영역을 지정하기 위해 동기화 메소드를 제공한다. 내부의 동기화 메소드를 실행하면 다른 스레드가 동기화 메소드를 실행하지 못하게 한다.
본 내용은 #혼자공부하는자바 책을 참고해 공부하려 작성했습니다.
'Work & Study > JAVA (& 혼공자Java)' 카테고리의 다른 글
자바(Java) - 스레드 제어2 (0) | 2021.01.07 |
---|---|
자바(Java) - 스레드 제어1 (0) | 2021.01.07 |
자바(Java) - 스레드4 (스레드 이름) (0) | 2020.12.21 |
자바(Java) - 스레드3 (toolkit.beep(); 비프음 실행) (0) | 2020.12.21 |
자바(Java) - 스레드2 (toolkit.beep(); 비프음 실행) (0) | 2020.12.15 |