Я просматриваю исходный код компилятора clojure и столкнулся с поведением, которое не соответствует исходному коду.
Класс PersistentVector
в clojure.lang
(https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentVector.java),, который реализован как трехраздельное дерево, имеет следующие поля:
- cnt (int)
- Хвост (Объект [])
- root (Node)
- shift (int)
- _meta
Node
имеет два поля. edit
и array
.
array
хранит дочерние элементы (ветви дерева), а edit
- это AtomicReference<Thread>
. edit
содержит null
для PersistentVector
с и идентификатор потока создания для TransientVector
с. В этом блоге очень хорошо объясняется, как на самом деле работают переходные процессы. Ключевой момент, который относится к моему запросу:
- Любые новые узлы, созданные с помощью переходного процесса, будут иметь поле
edit
, установленное в корне переходного процесса, который содержит идентификатор потока создания
- , когда
persistent!
вызывается на TransientVector
, edit
корневого узла устанавливается равным нулю, и PersistentVector
создается из этого корня, который возвращается. Такое поведение можно увидеть здесь в исходном коде.
Обратите внимание, что в поле edit
только корневого узла установлено значение null, поэтому любые новые узлы, созданные с помощью переходного процесса, с edit
, для которого установлен идентификатор потока, останутся неизменными. Поэтому я написал следующий код для проверки этого случая:
package clojure.lang;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<>();
for (int i=0; i<32; i++) {
arr.add(i);
}
PersistentVector pv1 = PersistentVector.create((Iterable)arr);
PersistentVector.TransientVector tv1 = pv1.asTransient();
tv1.conj(-1);
PersistentVector.Node n = (PersistentVector.Node) tv1.root.array[0];
System.out.println(n.edit);
PersistentVector pv2 = tv1.persistent();
n = (PersistentVector.Node) pv2.root.array[0];
System.out.println(n.edit);
System.out.println("end");
}
}
Вывод этого кода:
Thread[main,5,main]
null
end
Вот что делает этот код:
Создает PersistentVector
с 32 элементами. Этот вектор будет иметь полный хвост, поэтому добавление любого элемента приведет к созданию нового узла
Создано TransientVector
из него.
Добавляет элемент в переходный процесс. Это приведет к созданию нового узла, который будет храниться как дочерний элемент root
.
печатает значение поля edit
этого нового узла
делает переходный процесс persistent
печатает поле edit
того же узла.
Значение этого поля обнуляется! Я не могу понять, как это происходит. Я попытался установить точку наблюдения поля в intellij, но эта точка останова вообще не срабатывает при вызове tv1.persistent()
. В чем причина такого поведения? Я пробовал это с векторами с большим количеством элементов, и значение поля edit
сбрасывается для всех дочерних узлов, когда persistent
вызывается на TransientVector
.