-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
3 Weeks - [ConcurrentModification 동작 원리] #49
Comments
17 java docs에 명확히 정리하고 있어서 전체 내용을 가져왔습니다
객체의 동시 수정이 허용되지 않을 때, 해당 객체의 수정이 감지된 메서드에 의해 발생된다.
예로, 한 스레드가 Collection을 수정하고 다른 스레드가 동시에 iterate하려고 할 때 이는 일반적으로 허용되지 않는다. -> 자바에서 제공되는 컬렉션 라이브러리는 예외를 던지는 목적을 이해했으니 for-each 구현체와 Iterator 구현체를 보면서 어떤 상황에 예외를 던지는 지 확인해보았습니다 (List 인터페이스 기준으로 알아보았습니다) for (Transaction transaction : transactions) {
if(Character.isDigit(transaction.getReferenceCode().charAt(0))) {
transactions.remove(transaction); // ConcurrentModificationException!
}
} 내부 동작 코드 for (Iterator<Transaction> iterator = transactions.iterator();
iterator.hasNext(); ) {
Transaction transaction = iterator.next();
if(Character.isDigit(transaction.getReferenceCode().charAt(0))) {
transactions.remove(transaction); // 반복하면서 별도의 두 객체를 통해 컬렉션을 바꾸고 있는 문제
}
} 그렇다면 Iterator의 메서드는 어떻게 동작하기에 조회하는 컬렉션의 remove 상태를 파악할 수 있을까? ArrayList 구현체의 Iterator를 살펴보았습니다 public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; // The modCount value that the iterator believes that the backing List should have.
// prevent creating a synthetic constructor
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
내부로직을 통해
싱글 스레드에서 그렇다면 결국 정리하면
추가로 한 가지 더 고려사항이 있습니다
-> fail-fast 동작은 동기화되지 않은 동시 수정의 존재에서 어떠한 보장도 제공할 수없으며 fail-fast 연산은 최선의 노력으로 따라서 수정에 대해서는 modCount를 세지 않고 이에 대한 추적, 예외처리는 불가능합니다 -> 멀티 스레드 환경에서 Thread safe 하지 못한 컬렉션인 이유가 될 것 같습니다 //ArrayList 메서드
public E remove(int index) {
Objects.checkIndex(index, size);
checkForComodification();
E result = root.remove(offset + index);
updateSizeAndModCount(-1);
return result;
}
private void updateSizeAndModCount(int sizeChange) {
SubList<E> slist = this;
do {
slist.size += sizeChange;
slist.modCount = root.modCount;
slist = slist.parent;
} while (slist != null);
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
root.set(offset + lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
} public class Test {
static class TestThread implements Runnable {
List<String> list;
String name;
public TestThread(List<String> list, String name) {
this.list = list;
this.name = name;
}
@Override
public void run() {
int idx = 0;
for (String str : list) {
System.out.println("before in " + name +": " + str);
list.set(idx, "change by " + name);
System.out.println("after in " + name +": " + list.get(idx) + " idx: "+(idx++ + 1));
}
}
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("str1");
list.add("str2");
list.add("str3");
Thread t1 = new Thread(new TestThread(list, "thread1"));
Thread t2 = new Thread(new TestThread(list, "thread2"));
t1.start();
t2.start();
}
}
/* sout
before in thread1: str1
before in thread2: str1
after in thread2: change by thread1 idx: 1 ---
before in thread2: str2
after in thread2: change by thread2 idx: 2
after in thread1: change by thread1 idx: 1
before in thread2: str3
before in thread1: change by thread2 ---
after in thread2: change by thread2 idx: 3
after in thread1: change by thread1 idx: 2
before in thread1: change by thread2 ---
after in thread1: change by thread1 idx: 3
*/ 공유 자원 수정 시 이에 대한 일관성을 보장하지 못합니다 정리
Reference |
문제
contents - 세부 내용
어떤 상황들에서 발생하는 건지 정확히 알지 못했는데 이번 기회에 확실히 알고 넘어가면 좋을 것 같습니다!
참고
The text was updated successfully, but these errors were encountered: