线程安全的集合(一)

除了.net自带的Concurrent系列的线程安全集合,有时候我们可以有自定义的实现,比如使用锁(lock),但是这使得并发性下降。本文将利用Interlocked类实现线程安全的队列。

首先定义一个辅助类Node,这个节点类将作为队列中的元素。

private class Node
{
    public T Value;
    public Node Next;

    public Node(T value)
    {
        Value = value;
    }
}   

其中,我们定义了节点值属性Value,以及表示下一个节点的引用。

有了这个节点类,我们可以不需要使用.net框架的Queue类就能实现集合。

这个节点类与Haskell的List类型有点相似,比如[x, [x, [x, [x, [x]]]]]。假设给出一个头节点和一个尾节点,这个头节点是[x, [x, [x, [x, [x]]]]],尾节点的Next节点为null,从头节点开始,通过一级一级的引用,就能获取所有的节点值,直到遇到尾节点的Next节点null为止。

为了实现线程安全的队列,我们此时给出想象中的类以及部分字段的样子,如下

public class InterlockedQueue<T>
{
    // other members
    private Node head;
    private Node tail;
    public InterlockedQueue()
    {
        Node node = new Node(default(T));
        head = tail = node;
    }
}           

注意:我们需要把Node类作为InterlockedQueue类的私有类,才能实现参数化类型。

有了存储队列元素的结构之后,剩下的就是考虑如何Enqueue和Dequeue的线程安全操作了,利用前面提到的Interlocked类很容易实现。

Enqueue方法实现

public void Enqueue(T value)
{
    Node node = new Node(value);

    while(true)
    {
        Node tail = this.tail;    // Get current tail node
        Node next = tail.Next;     // Get current tail‘s next node

        // must be consistent. If not,  re-enter this while loop
        if (object.ReferenceEquals(tail, this.tail))
        {
            // next node of tail must be null, otherwise, other node is inserted, and it needs to re-enter this while loop
            if (object.ReferenceEquals(next, null)
            {
                // begin to insert this node
                if (object.ReferenceEquals(Interlocked.CompareExchange(ref tail.Next, node, next), next)  // (1)
                {
                    // if consistent, execute insert operation and then break this while loop
                    Interlocked.CompareExchanged(ref this.tail, node, tail);
                    break;
                }
            }
            else    // tail was not pointing to last node
                    //  try to swing Tail to the next node
                Interlocked.CompareExchange(ref this.tail, next, tail);
         }
    }
}

主要思想是将tail的Next节点指向新节点node,然后再讲tail指向node节点。

注意:(1)处的object.ReferenceEquals方法主要是用于判断是否已经交换,如果没有交换,那必然这个if判断为false。

Dequeue方法实现

public bool Dequeue(out T value)
{
    Node head;
    Node tail;
    Node next;

    while (true)
    {
        // read head
        head = this.head;
        tail = this.tail;
        next = head.Next;

        // Are head, tail, and next consistent?
        if (Object.ReferenceEquals(this.head, head))
        {
            // is tail falling behind
            if (Object.ReferenceEquals(head.Next, tail.Next))
            {
                // is the queue empty?
                if (Object.ReferenceEquals(next, null))
                {
                    value = default(T);

                    // queue is empty and cannot dequeue
                    return false;
                }

                Interlocked.CompareExchange<Node>(
                    ref this.tail,
                    next.Next,
                    tail);
            }
            else // No need to deal with tail
            {
                // read value before CAS otherwise another deque might try to free the next node
                value = next.Value;

                // try to swing the head to the next node
                if (Interlocked.CompareExchange<Node>(
                    ref this.head,
                    next,
                    head) == head)
                {
                    return true;
                }
            }
        }
    }
}

源代码参考网络。

时间: 2024-10-12 22:42:08

线程安全的集合(一)的相关文章

.NET 同步与异步 之 线程安全的集合 (十一)

本随笔续接:.NET 同步与异步 之 警惕闭包(十) 无论之前说的锁.原子操作 还是 警惕闭包,都是为安全保驾护航,本篇随笔继续安全方面的主题:线程安全的集合. 先看一下命名空间:System.Collections.Concurrent,常用的类型有(均为泛型):BlockingCollection<T>.ConcurrentBag<T>.ConcurrentDictionary<TKey, TValue>.ConcurrentQueue<T>.Concu

Concurrent包总结——线程安全的集合操作

java中提供了丰富的集合类操作,大概可以分为无序结合Set,有序集合List和无序键值对集合Map.Java5之后又新增了队列操作集合Queue.Java1.5之后新增了线程安全的集合操作类,阻止在java.util.concurrent包中.本文仅仅探讨该包下面的线程安全的结合操作类. 先看下concurrent包下面线程安全类的类图结构: 1.CopyOnWriteArraySet类 CopyOnWriteArrayList类的底层是通过CopyOnWriteArrayList来实现的.因

java中线程安全的集合对象

Vector与ArrayList Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销. HashTable与HashMap Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的.在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了. StringBuilder与StringBuffer StringBuilder和StringBuffer的方法是一模一样,就前者是多

java多线程(十)使用线程安全的集合

转载请注明出处:http://blog.csdn.net/xingjiarong/article/details/48046751 在多线程中,如果要并发的修改一个数据结构,那么很有可能会破坏这个数据结构.例如,一个线程可能要向一个散列表中插入一个元素,假如在调整各个桶之间的链接关系时被剥夺了控制权,而此时正好有另外一个线程正在遍历链表,则可能会产生异常或者死循环. 可以通过锁来保护共享的数据结构,但是选择线程安全的实现作为替代可能更容易一些. 一.旧的线程安全的集合 任何集合类都可以通过使用同

Java核心技术 卷1 多线程----线程安全的集合(4)

如果多线程要并发的修改一个数据结构,例如散列表,那么很容易会破坏这个数据结构.一个线程可能要开始向表中插入一个新元素.假定在调整散列表各个桶之间的链接关系的过程中,被剥夺了控制权.如果另一个线程也开始遍历同一个链表,可能使用无效的链接并造成混乱,会抛出异常或者陷入死循环. 可以通过提供锁来保护共享数据结构,但是选择线程安全的实现作为替代可能更容易些.上一篇讨论的阻塞队列就是线程安全的集合.接下来讨论java类库提供的另外一些线程安全的集合. 高效的映射表.集合和队列 java.util.conc

一个主线程下有多个子线程任务,主线程必须在100秒内将子线程执行的集合结果进行处理返回

实现代码: package cmd.chengxuyuanzhilu.async; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future;

线程安全的集合操作类

常见的操作接口有:Map,List,Set,Vector 其最常用的实现类有:HashMap,ArrayList,LinkedList,HashSet 但是只有Vector是线程安全的,Collections实现了一个些方法可以保证常用的集合类达到线程安全: Map: Map<Object,Object> map =  Collections.synchronizedMap(new HashMap<>()); Set:   Set<Object> set1 = Coll

把非线程安全的集合转换为线程安全

ArrayList是非线程安全的,换句话说,多个线程可以同时进入一个ArrayList对象的add方法 借助Collections.synchronizedList,可以把ArrayList转换为线程安全的List. 与此类似的,还有HashSet,LinkedList,HashMap等等非线程安全的类,都通过工具类Collections转换为线程安全的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package multiplethread; import java.ut

阿里P7分享最新java面试——线程面试题集合

1.说说进程,线程,协程之间的区别 简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程.进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高.线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位.同一进程中的多个线程之间可以并发执行. 2.你了解守护线程吗?它和非守护线程有什么区别 程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程.守护线程最典型的例子就是GC线