Java 5.0에서 새로운 synchronize 기능
- Posted at 2007/07/12 23:44
- Filed under Program/JAVA
멀티 쓰레드 프로그램에서 객체의 상태정보(멤버변수)에 대해 여러 쓰레드가 동시에 접근이 가능한 경우에는 반드시 synchronized 등을 이용하여 쓰레드간에 동기화 처리를 해야 한다.
이 동기화 처리는 객체의 값을 변경할 때 뿐만 아니라 값을 읽을 때도 해야 한다.
예를 들어 List values 라는 멤버변수와 관련된 처리를 하는 다음 객체를 여러 쓰레드에서 동시에 접근하는 경우 문제가 발생할 수 있다.
public class SyncTest {
List<String> values = new ArrayList<String>();
public void test() {
Thread writeThread = new Thread() {
public void run() {
while(true) {
for(int i = 0; i < 10; i++) {
add("Val" + i);
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
};
Thread readThread = new Thread() {
public void run() {
while(true) {
print();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
writeThread.start();
readThread.start();
}
public void add(String value) {
values.add(value);
}
public void print() {
for(String value: values) {
System.out.println(value);
}
}
public static void main(String[] args) {
(new SyncTest()).test();
}
}
한 쓰레드가 for 반복문을 수행하면서 values의 값을 읽는 동안 다른 쓰레드가 values에 값을 추가하게 되면 읽기를 수행하는 메소드에서 ConcurrentModificationException이 발생하게 된다. 따라서 읽기 연산을 처리하는 메소드내에서도 동기화 처리를 해주어야 한다.
public synchronized void add(String value) {
values.add(value);
}
public synchronized void print() {
for(String value: values) {
System.out.println(value);
}
}
동기화 작업을 위해 읽기 작업에 synchronized를 이용하게 되면 하나의 쓰레드가 읽고 있는 경우에 다른 쓰레드는 읽기 작업을 할 수 없게 된다. 대부분의 객체들의 멤버변수는 값에 대한 변경 작업은 드물고 읽기 작업이 빈번한 경우가 많다. 이런 경우에는 synchronized 키워드를 이용하여 동기화를 할 경우 성능에 영향을 미치게 된다.
Java5.0 부터는 이런 문제를 해결하기 위해 몇가지 새로운 동기화 개념을 지원하고 있다.
synchronized는 read/write 작업에 상관없이 모든 오퍼레이션에 대해 blocking 처리를 한다. 반면 Lock interface의 경우 read와 write에 대해 개별적으로 동기화 처리를 할 수 있게 해준다.
write lock을 걸게되면 read 오퍼레이션도 못하게 되지만, read lock을 걸게될 경우 다른 쓰레드의 read 오퍼레이션은 허용이 되기 때문에 멀티쓰레드 환경에서 read 작업이 많은 연산의 경우 성능에 영향을 주지 않으면서 동기화를 처리할 수 있게 된다. 사용방법은 다음과 같다.
List<String> values = new ArrayList<String>();
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void add(String value) {
lock.writeLock().lock();
try {
values.add(value);
} finally {
lock.writeLock().unlock();
}
}
public void print() {
lock.readLock().lock();
try {
for(String value: values) {
System.out.println(value);
}
} finally {
lock.readLock().unlock();
}
}
주의 해야할 사항은 lock 호출 내부나 호출 되는 메소드에서 다시 lock을 걸게 되는 경우 lock을 가져오지 못하면 deadlock이 발생하게 된다.
deadlock 상황을 피하기 위해 일정시간 이상동안 lock을 얻지 못하는 경우 Exception을 발생하게 하는 기능도 추가되었다.
public void print() {
try {
if(lock.readLock().tryLock(100, TimeUnit.MILLISECONDS)) {
try {
for(String value: values) {
System.out.println(value);
}
} finally {
lock.readLock().unlock();
}
} else {
System.out.println("Lock Timeout");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Posted by 김형준
- Response
- No Trackback , 2 Comments
Trackback URL : http://www.jaso.co.kr/trackback/177
Comments List
-
이런 것이 있었군요 ^^ 오늘도 배워갑니다.
-
좋은게 있었군요.






