- 스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없도록, 객체에 잠금을 걸어주어야 한다.
- 임계 영역(critical section)을 설정해서 문제 해결 -> lock 개념 도입
- 임게 영역 : 단 하나의 스레드만 실행할 수 있는 코드 영역
스레드 동기화 방법
- 임계 영역(critical section) : 공유 자원에 단 하나의 스레드만 접근하도록(하나의 프로세스에 속한 스레드만 가능)
- 뮤텍스(mutext) : 공유 자원에 단 하나의 스레드만 접근 가능하도록(서로 다른 프로세스에 속한 스레드도 가능)
- 이벤트(event) : 특정한 사건 발생을 다른 스레드에게 알린다.
- 세마포어(semaphore) : 한정된 개수를 가진 자원을 여러 스레드가 사용하려고 할 때 접근 제한
- 대기 기능 타이머(waitable timer) : 특정 시간이 되면 대기 중이던 스레드를 꺠운다.
동기화를 하지 않은 Code
- 스레드 2개가 동시에 실행되기 때문에 잔액이 마이너스가 나온다.
동기화 사용 synchronzied keywowrd
- 메소드 자체에 동기화를 하거나, 임의 영역을 설정해서 block으로 닫아주는 방법이 있다.
- 임의 영역을 설정해서 동기화 해주는 방식이 좀 더 사용하기 좋다.
- 공유 객체가 자기 자신인 경우 -> this
상태 전이도
- wait() : lock을 걸고 대기
notify() Method
- 스레드가 작업을 완료하면 notifiy를 호출해서 일시 정지 상태의 다른 스레드들을 실행 대기 상태로 옮긴다.
- 작업 완료된 스레드(자기 자신)는 wait() 메소드로 일시 정지 상태로 만든다.
- wait에서 대기하고 있는 스레드가 실행할 수 있는 상황이 되어서 block 해제
Java 고유 락(Intrinsic LocK)
자바의 모든 객체는 lock을 갖고 있다.
synchronzied 블록은 Intrinsic lock(고유락)을 사용해서 스레드 접근을 제어한다.
synchronzied()
1. 인자 값에 this 이용 : 여러 스레드가 들어와 서로 다른 block 호출해도 this를 사용해서 자기 자신에 lock을 걸어 기다려야 한다.
2. 인자 값에 Object 이용 : block 마다 다른 Lock을 걸리게 해 효율적인 코드 작성 가능
structured lock(구조적 lock) : 고유 lock을 통한 동기화
reentarant lock(명시적 lock) : A획득 -> B획득 -> A해제 -> B해제를 가능하게 하기 위해서 사용
public class Counter{
private Object lock = new Object(); // 모든 객체가 가능 (Lock이 있음)
private int count;
public int increase() {
// 단계 (1)
synchronized(lock){ // lock을 이용하여, count 변수에의 접근을 막음
return ++count;
}
/*
단계 (2)
synchronized(this) { // this도 객체이므로 lock으로 사용 가능
return ++count;
}
*/
}
/*
단계 (3)
public synchronized int increase() {
return ++count;
}
*/
}
class Exercise3_1 {
public static void main(String[] args){
int x = 2;
int y = 5;
char c = 'A'; // 'A'의 문자 코드는 65
System.out.println(1 + x << 33);
System.out.println(y >= 5 || x < 0 && x > 2);
System.out.println(y += 10 - x++);
System.out.println(x+=2);
System.out.println(!('A' <= c && c <= 'Z'));
System.out.println('C'-c);
System.out.println('5'-'0');
System.out.println(c+1);
System.out.println(++c);
System.out.println(c++);
System.out.println(c);
}
}
풀이
1. 1+2 (3를 33만큼 왼쪽으로 이동 -> (32+1bit)1번만 이동 0011 -> 0110 = 6 2. true || false && false = false 논리연산자 &&가 ||보다 우선순위가 더 높다 true || true = true 3. 15-2++ = 13 4. (2++) + 2 = 5 5. !(true && true) = false 6. 67-65 = 2 7. 5 8. 66 9. B 10. B 11. C
[3-2] 아래의 코드는 사과를 담는데 필요한 바구니(버켓)의 수를 구하는 코드이다. 만일 사과의 수가 123개이고 하나의 바구니에는 10개의 사과를 담을 수 있다면, 13개의 바구니가 필요할 것이다. (1)에 알맞은 코드를 넣으시오.
class Exercise3_2 {
public static void main(String[] args){
int numOfApples = 123; // 사과의 개수
int sizeOfBucket = 10;
// 바구니의 크기(바구니에 담을 수 있는 사과의 개수)
int numOfBucket = ( /*(1)*/ );
// 모든 사과를 담는데 필요한 바구니의 수
System.out.println("필요한 바구니의 수 :"+numOfBucket);
}
}
[3-3] 아래는 변수 num의 값에 따라 ‘양수’, ‘음수’, ‘0’을 출력하는 코드이다. 삼항 연산자를 이용해서 (1)에 알맞은 코드를 넣으시오. (삼항 연산자 2번 사용)
class Exercise3_3 {
public static void main(String[] args){
int num = 10;
System.out.println( /* (1) */ );
}
}
[실행결과]
양수
풀이
(num>0) ? "양수" : ((num<0) ? "음수" : '0')
[3-4] 아래는 변수 num의 값 중에서 백의 자리 이하를 버리는 코드이다. 만일 변수 num의 값이 ‘456’이라면 ‘400’이 되고, ‘111’이라면 ‘100’이 된다. (1)에 알맞은 코드를 넣으시오.
class Exercise3_4 {
public static void main(String[] args){
int num = 456;
System.out.println( /* (1) */ );
}
}
[실행결과]
400
풀이
(num/100)*100
[3-5] 아래는 변수 num의 값 중에서 일의 자리 1로 바꾸는 코드이다. 만일 변수 num의 값이 ‘333’이라면 ‘331’이 되고, ‘777’이라면 ‘771’이 된다. (1)에 알맞은 코드를 넣으시오.
class Exercise3_5 {
public static void main(String[] args){
int num = 333;
System.out.println( /* (1) */ );
}
}
[실행결과]
331
풀이
((num/10)*10)+1
[3-6] 아래는 변수 num의 값보다 크면서도 가장 가까운 10의 배수에서 변수 num의 값을 뺀 나머지를 구하는 코드이다. 예를 들어, 24의 크면서도 가장 가까운 10의 배수는 30이다. 19의 경우 20이고, 81의 경우 90이 된다. 30에서 24를 뺀 나머지는 6이기 때문에 변수 num의 값이 24라면 6을 결과로 얻어야 한다. (1)에 알맞은 코드를 넣으시오.
class Exercise3_6 {
public static void main(String[] args){
int num = 24;
System.out.println( /* (1) */ );
}
}
[실행결과]
6
풀이
(((num/10)+1)*10) - num
[3-7] 아래는 화씨(Fahrenheit)를 섭씨(Celsius)로 변환하는 코드이다. 변환공식이 ‘C’ = 5/9 x (F - 32)’ 라고 할 때, (1)에 알맞은 코드를 넣으시오. 단, 변환 결과값은 소수점 셋째자리에서 반올림해야한다. (Math.round()를 사용하지 않고 처리할 것)
class Exercise3_7 {
public static void main(String[] args){
int fahrenheit = 100;
float celcius = ( /* (1) */ );
System.out.println("Fahrenheit:"+fahrenheit);
System.out.println("Celcius:"+celcius);
}
}
[3-10] 다음은 대문자를 소문자로 변경하는 코드인데, 문자 ch에 저장된 문자가 대문자인 경우에만 소문자로 변경한다. 문자코드는 소문자가 대문자보다 32만큼 더 크다. 예를들어 ‘A’의 코드는 65이고, ‘a’의 코드는 97이다. (1)~(2)에 알맞은 코드를 넣으시오.
[2-8] 참조형 변수(reference type)와 같은 크기의 기본형(primitive type)은? (모두 고르시오)
int
long
short
float
double
풀이
참조형 변수는 64bit JVM을 사용하면 8byte : double
32bit JVM을 사용하면 4byte : int, float
[2-9] 다음 중 형 변환을 생략할 수 있는 것은? (모두 고르시오)
byte b = 10;
char ch = 'A;
int i = 100;
long l = 1000L;
a. b = (byte)i;
b. ch = (char)b;
c. shrot s = (short)ch;
d. float f = (flaot)l;
e. i = (int)ch;
풀이
int(4) → byte(1) 크기가 작기 때문에 불가능
byte(1) → char(2) 음수는 변환 불가능
char(2) → short(2) 크기는 같지만, 음수 변환 불가능
long(8) → float(4) 크기가 작아서 실수 생략 가능하므로 가능
char(2) - int(4) 가능
[2-10] char 타입의 변수에 저장될 수 있는 정수 값의 범위는? (10진수로 적으시오)
풀이
0~65536 = 2^16-1
[2-11] 다음 중 변수를 잘못 초기화 한 것은? (모두 고르시오)
a. byte b = 256;
b. char c = '';
c. char answer = 'no';
d. float f = 3.14
e. double d = 1.4e3f;
풀이
byte : -128~127 불가능
char : 한개의 문자가 아니라 불가능
char : 두개의 문자 불가능
float : 리터럴 뒤에 f가 오지 않아서 불가능
[2-12] 다음 중 main 메서드의 선언부로 알맞은 것은? (모두 고르시오)
a. public static void main(String[] args)
b. public static void main(String args[])
c. public static void main(String[] arv)
d. public void static main(String[] args)
e. static public void main(String[] args)
풀이
a, b, c, e
[2-13] 다음 중 타입과 기본값이 잘못 연결된 것은? (모두 고르시오)
a. boolean - false
b. char - '\\u0000' // 16진수 저장
c. float - 0.0
d. int - 0
e. long - 0
f. String - ""