邓心一 há 3 anos atrás
pai
commit
0703ab0914

+ 168 - 0
basis/algorithms/skiplist/java/ConcurrentSkipList.java

@@ -0,0 +1,168 @@
+import sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class ConcurrentSkipList<K extends Comparable<K>, V> {
+    private static Unsafe unsafe = getUnsafe();
+    private static long valueOffset;
+    private static long nextOffset;
+    static {
+        try {
+            valueOffset = unsafe.objectFieldOffset(Node.class.getDeclaredField("value"));
+            nextOffset = unsafe.objectFieldOffset(Node.class.getDeclaredField("next"));
+        } catch (Exception e) {
+            // Ignore
+        }
+    }
+
+    private final ThreadLocalRandom random;
+    private final int maxLevel;
+    private final double p;
+
+    private volatile Index<K, V> head;
+
+    public ConcurrentSkipList(int maxLevel, double p) {
+        random = ThreadLocalRandom.current();
+        this.maxLevel = maxLevel;
+        this.p = p;
+        Node<K, V> node = new Node<>(null);
+        head = new Index<>(node);
+    }
+    
+    public V put(K key, V value) {
+        Stack<Index<K, V>> update = new Stack<>();
+        Index<K, V> curr = head;
+        while (curr != null) {
+            while (curr.right != null && cmp(key, curr.right.node.key) > 0) {
+                curr = curr.right;
+            }
+            update.add(curr);
+            curr = curr.down;
+        }
+        Node<K, V> p = update.peek().node;
+        while (p.next != null && cmp(key, p.next.key) >= 0) {
+            p = p.next;
+        }
+        if (p.key != null && cmp(key, p.key) == 0) {
+            // update value only
+            while (!unsafe.compareAndSwapObject(p, valueOffset, p.value, value)) {
+            }
+            return value;
+        }
+        Node<K, V> after = putAfter(p, key, value);
+        while (!update.isEmpty()) {
+            update.pop();
+        }
+        return value;
+    }
+
+    public boolean containsKey(K key) {
+        Index curr = head;
+        while (curr != null) {
+            while (curr.right != null && cmp(key, curr.right.node.key) > 0) {
+                curr = curr.right;
+            }
+            if (curr.down == null) {
+                break;
+            } else {
+                curr = curr.down;
+            }
+        }
+        Node node = curr.node;
+        while (node.next != null && cmp(key, node.next.key) > 0) {
+            node = node.next;
+        }
+        return node.next != null && cmp(key, node.next.key) == 0;
+    }
+
+    public V get(K key) {
+        Index<K, V> curr = head;
+        while (curr != null) {
+            while (curr.right != null && cmp(key, curr.right.node.key) > 0) {
+                curr = curr.right;
+            }
+            if (curr.down == null) {
+                break;
+            } else {
+                curr = curr.down;
+            }
+        }
+        Node<K, V> node = curr.node;
+        while (node.next != null && cmp(key, node.next.key) > 0) {
+            node = node.next;
+        }
+        node = node.next;
+        if (node != null && cmp(key, node.key) == 0) {
+            return node.value;
+        }
+        return null;
+    }
+
+    private static Unsafe getUnsafe() {
+        try {
+            Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+            Field field = unsafeClass.getDeclaredField("theUnsafe");
+            field.setAccessible(true);
+            Unsafe unsafe = (Unsafe) field.get(null);
+            return unsafe;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private Node<K, V> putAfter(Node<K, V> before, K key, V value) {
+        Node<K, V> after = new Node<>(key, value);
+        while (true) {
+            Node<K, V> next = before.next;
+            after.next = next;
+            if (unsafe.compareAndSwapObject(before, nextOffset, next, after)) {
+                break;
+            }
+        }
+        return after;
+    }
+
+    private int cmp(Object o1, Object o2) {
+        return ((Comparable) o1).compareTo((Comparable) o2);
+    }
+
+    public static void main(String[] args) {
+        ConcurrentSkipList<Integer, String> skipList = new ConcurrentSkipList<>(20, 0.2);
+        skipList.put(1, "answer");
+        System.out.println(skipList.get(1));
+    }
+
+    private static class Node<K, V> {
+        private K key;
+        private V value;
+        private Node<K, V> next;
+
+        private Node(K key, V value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        private Node(Node<K, V> next) {
+            this.next = next;
+        }
+    }
+
+    private static class Index<K, V> {
+        private Node<K, V> node;
+        private Index<K, V> down;
+        private Index<K, V> right;
+
+        private Index(Node<K, V> node) {
+            this.node = node;
+        }
+    }
+}

+ 165 - 0
basis/algorithms/skiplist/java/SkipList.java

@@ -0,0 +1,165 @@
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class SkipList<T extends Comparable<T>> {
+    private final ThreadLocalRandom random;
+
+    private final int maxLevel;
+    private final AtomicInteger curLevel;
+    private final double p;
+    private final ListNode<T> head;
+
+    public SkipList(int maxLevel, double p) {
+        this.maxLevel = maxLevel;
+        this.p = p;
+        this.random = ThreadLocalRandom.current();
+        this.curLevel = new AtomicInteger(0);
+        this.head = new ListNode<T>(null, maxLevel);
+    }
+
+    public void add(T key) {
+        insert(key);
+    }
+
+    public void insert(T key) {
+        Stack<ListNode<T>> update = new Stack<>();
+        ListNode<T> curr = head;
+        for (int level = curLevel.get(); level >= 0; level--) {
+            while (curr.canMoveForward(key, level)) {
+                curr = curr.forward.get(level).get();
+            }
+            update.add(curr);
+        }
+        ListNode<T> next = curr.forward.get(0).get();
+        if (next != null && key.compareTo(next.key) == 0) {
+            // Update value
+            return;
+        }
+        ListNode<T> down = null;
+        int insertLevel = randomLevel();
+        for (int level = 0; level <= insertLevel; level++) {
+            curr = update.isEmpty() ? head : update.pop();
+            ListNode<T> up = insertAfter(key, curr, level);
+            if (level > 0) {
+                up.forward.get(level - 1).set(down);
+            }
+            down = up;
+        }
+        while (true) {
+            int expectedLevel = curLevel.get();
+            if (expectedLevel >= insertLevel || curLevel.compareAndSet(expectedLevel, insertLevel)) {
+                break;
+            }
+        }
+    }
+
+    public boolean contains(T key) {
+        ListNode<T> curr = head;
+        for (int level = curLevel.get(); level >= 0; level--) {
+            while (curr.canMoveForward(key, level)) {
+                curr = curr.forward.get(level).get();
+            }
+        }
+        curr = curr.forward.get(0).get();
+        return curr != null && key.compareTo(curr.key) == 0;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("current level: ").append(curLevel.get()).append("\n");
+        for (int i = curLevel.get(); i >= 0; i--) {
+            ListNode<T> curr = head.forward.get(i).get();
+            while (curr != null) {
+                sb.append(curr.key.toString()).append(" ");
+                curr = curr.forward.get(i).get();
+            }
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+
+    private ListNode<T> insertAfter(T key, ListNode<T> before, int level) {
+        ListNode<T> after = new ListNode<>(key, level);
+        while (true) {
+            while (before.canMoveForward(key, level)) {
+                before = before.forward.get(level).get();
+            }
+            ListNode<T> next = before.forward.get(level).get();
+            after.forward.get(level).set(next);
+            if (before.forward.get(level).compareAndSet(next, after)) {
+                break;
+            }
+        }
+        return after;
+    }
+
+    private int randomLevel() {
+        int level = 0;
+        while (level < maxLevel) {
+            if (random.nextDouble() < p) {
+                level++;
+            } else {
+                break;
+            }
+        }
+        return level;
+    }
+
+    public static void main(String[] args) throws InterruptedException {
+        SkipList<Integer> skipList = new SkipList<>(20, 0.2);
+//        Set<Integer> skipList = new ConcurrentSkipListSet<>();
+        int threads = 1000;
+        int loop = 100;
+        ExecutorService executor = Executors.newCachedThreadPool();
+        CountDownLatch cd = new CountDownLatch(threads);
+        for (int i = 0; i < threads; i++) {
+            final int initJ = i;
+            executor.submit(() -> {
+                for (int j = initJ; j < threads * loop; j += threads) {
+                    skipList.add(j);
+                }
+                cd.countDown();
+            });
+        }
+        cd.await();
+        executor.shutdown();
+        for (int i = 0; i < threads * loop; i++) {
+            if (!skipList.contains(i)) {
+                System.out.println("Concurrent error: " + i);
+            }
+        }
+    }
+
+    public static class ListNode<T extends Comparable<T>> {
+        private List<AtomicReference<ListNode<T>>> forward;
+        private T key;
+
+        public ListNode(T key, int level) {
+            this.key = key;
+            this.forward = new ArrayList<>(level + 1);
+            for (int i = 0; i <= level; i++) {
+                this.forward.add(new AtomicReference<>());
+            }
+        }
+
+        public boolean canMoveForward(T key, int level) {
+            return this.forward.get(level).get() != null &&
+                    key.compareTo(this.forward.get(level).get().key) > 0;
+        }
+
+        @Override
+        public String toString() {
+            return "ListNode{" +
+                    "forward=" + forward +
+                    ", key=" + key +
+                    '}';
+        }
+    }
+}