Observable
이란 코드 기반으로 이루어지는데,ObservableSet
를 생성후 element 를 add 할때 원소가 Set 에 추가되면서 이제 addNotifyElement
구독자들에게 원소가 추가됬다는 사실을 알려주기 위해서 이제 callback 함수인 added
가 발생하는데, 근데 여기서 그 람다로 정의된 함수를 보면 아래와 같이 되어 있습니다.set.addObserver(new SetObserver<>() {
public void added(ObservableSet<E> set, E element){
print(element); // 귀찮아서 이렇게 적었습니다.
if(e == 23)
s.removeObserver(this);
}
});
참고 SetObservers
@FunctionalInterface
public interface SetObserver<E> {
//Call this function when ObservableSet added element
void added(ObservableSet<E> set, E element);
}
added
는 콜백함수이다. 근데 지금 하나의 스레드 자체는 observers
에 lock 을 걸고 순회중인데, 갑자기 외부에서 해당 observer 에 접근이 가능하게 된것이다. 그니까 현재 notifyElementAdded
가 block synchronized 구조 인데public void notifyElementAdded(E element) {
synchronized (observers) {
for (SetObserver<E> observer : observers) {
observer.added(this, element);
}
}
}
저런 구조 자체가 added
라는 callback 함수의 내부까지 synchronized 가 잡히지 않기 때문입니다. 실제로 진행해보면 이걸 Debugging 으로 하나하나씩 잡아보면, 처음에 add 에 들어오고 조건문을 거쳐 notifyElementAdded
로 가게 되고, 그 이후 observers 에 lock 을 걸고 observers 를 순회하게 되는데 이때 observer 의 added 메소드로 가게됩니다. (아직 끝나기전) 그래서 지금 메소드 스택구조를 간단하게 그려보면
----------------------
| remove |
----------------------
| removeObservers |
----------------------
| added |
----------------------
| notifyElementAdded | => observers 에 lock 을 걸어주었음
----------------------
| add |
----------------------
인데 지금 added
에서 remove
를 호출하여 notifyElementAdded
에서 작업하고 있는 observers 에 대한 순회 도중 변경이 일어났음 따라서 ConcurrentModificationException
발생하고 종료됨.
우리의 경우 List 이므로 for-loop 는 Iterator
로 작동하는데 Iter 클래스는 AbstractList
(or List 등등 Collection 구조에 따라다를것임) 는 처음에 modCount
변수를 자신의 지역변수인 expectedModCount
에 할당하는데 이는 modCount 에 해당 Collection 에 관한 변경사항에 따른 count 를 기록하는데 add / remove 등등 다수의 메소드에서 기록됨. 그래서 next() 를 하는데 기존 자신이 참조하고 있는 Collection 과의 변경사항에 대해 Checkt 를 하는데 이때 다르면 ConcurrentModificationException
을 발생시키는데 지금과 같은 경우는 앞에서 add 를 해줘서 modCount++ 을 해준 상태(현재 상태 1)일텐데, 마지막에 remove 가 작동하여 modCount 를 ++ (2)해줍니다. 하지만 이미 exptectModCount 는 1 이므로 다음 next() 를 하는과정에서 ConcurrentModificationException
이 발생합니다. 이거 이해안가시면 당일날 디버깅으로 보여드리겠습니다. 직접보고싶으시면 [ArrayList.java](http://arraylist.java)
에 956 번째 줄 967 번째 줄 Break Point 로 잡고 확인하시면 됩니다.
Enhanced for loop 는 Itreator 를 랩핑하여 사용하는 것이다.
ArrayList.java 안의 private class Itr implements Iterator 을 계속해서 호출해 오는데 우리가 처음에 add 를 해서 expecteModCount 한 값이 들어가고, 그 다음에 이제 remove 가 콜백되어 modCount++ 이 되어 오류가 나는것이다 . 음 Enhanced for loop 도 아래와 상당히 흡사하게 돌아가거나 (같을 것이므로 아래 코드를 예시로 들면) 처음에 modCount 가 들어가는데 이때는 add 가 되어 1 이 들어가있을텐데 그 도중에 remove 가 발생되어 2가 될테고, 결국 마지막 if (modCount != expectedModCount)
에 걸려서 ConcurrentModificationException
을 발생시킨다.
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
final Object[] es = elementData;
final int size = this.size;
for (int i = 0; modCount == expectedModCount && i < size; i++)
action.accept(elementAt(es, i));
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
docker run -d -p 3307:3306 -v /Volumes/roach/docker/test:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=1234 --name testingdb mysql:8.0.17 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
Mysql 에서 Sample Data 를 제공하는데 Docker 안에서 해당 파일을 git 으로 다운받자
다운 받았으면 test_db 라는 디렉토리가 하나 생길 것 이다.
mysql -uroot -p<password> -t employee.sql
이런식으로 하면 무슨 뭐뭐뭐 뜨면서 막 업데이트 된다. 아 참고로 요즘 도커는 볼륨도 디폴트로 어딘가 지정해주는것 같긴하다? 옛날 버전은 날라갔었는데 근데 난 외장하드에 도커 환경을 구축하기 때문에 따로 볼륨 path 를 지정해주었다.
여튼 저 명령어를 치면 정상적으로 추가되어 있다~ DB 실습은 이렇게 해보자!
그리고 엄청 큰 샘플 데이터들도 있는데 그런거 받아서 인덱스 공부해보면 빠르게 감을 익힐 수 있다.