交替锁实现线程安全链表

交替锁实现线程安全链表

常见的线程安全链表

1、Synchronized

 @Deprecated
  public synchronized void insert(int value) {
      throw new UnsupportedOperationException();
  }
  • 使用synchronized内置锁,保证插入操作是线程安全的
    • 坏处:性能差
    • 好处:实现简单

2、ReentrantReadWriteLock

//使用伪代码
writeLock.lock();
insert(value);
writeLock.unlock();
  • 使用读写锁实现读写分离
    • 好处:读写分离,提高性能
    • 坏处:对于写来说,还是与内置锁性能类似

可能还有用Collecions工具类进行普通的list做线程安全封装的,但是从实质上来看,这些的性能都是差不多。

可以这么说,以上的安全实现都是锁整个链表,那么有没有其它方式呢,这就是我今天要分享的交替锁

交替锁的实现原理

交替锁就是使用两把锁,交替释放,从而保证链表中未加锁的部分能够被其它线程自由访问,大大提高了性能。特别是当链表很长的时候,一个插入操作的时间大概假设是N,此时如果有10个线程执行插入操作,原先锁整个链表需要花费10N,而使用交替锁,最好的情况是接近于N!

以下就是交替锁的算法图示:(自上而下)

img

交替锁的实现源码

/**
 * @author linxu
 * @date 2019/8/20
 * <tip>take care of yourself.everything is no in vain.</tip>
 * 利用交替锁实现并发的有序链表
 */
public class ConcurrentSortedList {
    private final Node head;
    private final Node tail;

    public ConcurrentSortedList(Node head, Node tail) {
        this.head = head;
        this.tail = tail;
    }

    public ConcurrentSortedList() {
        head = new Node();
        head.value=Integer.MAX_VALUE;
        tail = new Node();
        tail.value=Integer.MIN_VALUE;
        head.next = tail;
        tail.next = head;
    }

    /**
     * 内部结点
     */
    private class Node {
        int value;
        Node prev;
        Node next;
        ReentrantLock lock = new ReentrantLock(false);

        public Node() {
        }

        public Node(int value, Node prev, Node next) {
            this.next = next;
            this.prev = prev;
            this.value = value;
        }

    }
    /**
     * 使用交替锁实现
     *
     * @param value  v
     */
    public void insert(int value) {
        //从第一个节点开始加锁
        Node current = head;
        current.lock.lock();
        Node next = current.next;
        try {
            while (true) {
                next.lock.lock();
                try {
                    //插入的条件
                    if (next == tail || next.value < value) {
                        Node node = new Node(value, current, next);
                        next.prev = node;
                        current.next = node;
                        return;
                    }
                } finally {
                    //总是先释放前一把锁
                    current.lock.unlock();
                }
                current = next;
                next = current.next;
            }
        } finally {
          //释放最后的一把锁
            next.lock.unlock();
        }
    }
}

思考

为何在java.util中不是对链表这么加锁的,其实它有它的道理,因为java中的内置链表添加元素的添加到链表尾部的,直接获取尾部元素,然后执行tail.next=new node,因此正常情况下都是直接锁整个链表。但是在实现如以上的有序链表,这个时候是需要遍历整个链表,就可以采用交替锁,从而极大地优化了链表的插入性能。

发布了57 篇原创文章 · 获赞 32 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/rekingman/article/details/99860866