我们知道,在.net的一些集合类型中,譬如Hashtable和ArrayList,都有Synchronized静态方法和SyncRoot实例方法,他们之间有联系吗?我怎么才能用好他们呢?
我们以Hashtable为例,看看他们的基本用法:

            Hashtable ht  =  Hashtable.Synchronized( new  Hashtable());
             lock  (ht.SyncRoot)
             {
                //一些操作
            }

1,Synchronized表示返回一个线程安全的Hashtable,什么样的hashtable才是一个线程安全的呢?下边我们就从.NET的源码开始理解。

public   static  Hashtable Synchronized(Hashtable table)
{
    if (table == null)
    {
        throw new ArgumentNullException("table");
    }
    return new SyncHashtable(table);
}

从源码不难看出,Synchronized方法返回的其实是一个SynchHashtable类型的实例。在前边我们说过, Synchronized表示返回一个线程安全的Hashtable ,从这个解释不难看出,SynchHashtable应该是继承自Hashtable。下边我们验证一下。看看SynchHashtable类型的源码:

Code
[Serializable]
private class SyncHashtable : Hashtable
{
    // Fields
    protected Hashtable _table;

    // Methods
    internal SyncHashtable(Hashtable table) : base(false)
    {
        this._table = table;
    }

    internal SyncHashtable(SerializationInfo info, StreamingContext context) : base(info, context)
    {
        this._table = (Hashtable) info.GetValue("ParentTable", typeof(Hashtable));
        if (this._table == null)
        {
            throw new SerializationException(Environment.GetResourceString("Serialization_InsufficientState"));
        }
    }

    public override void Add(object key, object value)
    {
        lock (this._table.SyncRoot)
        {
            this._table.Add(key, value);
        }
    }

    public override void Clear()
    {
        lock (this._table.SyncRoot)
        {
            this._table.Clear();
        }
    }

    public override object Clone()
    {
        lock (this._table.SyncRoot)
        {
            return Hashtable.Synchronized((Hashtable) this._table.Clone());
        }
    }

    public override bool Contains(object key)
    {
        return this._table.Contains(key);
    }

    public override bool ContainsKey(object key)
    {
        return this._table.ContainsKey(key);
    }

    public override bool ContainsValue(object key)
    {
        lock (this._table.SyncRoot)
        {
            return this._table.ContainsValue(key);
        }
    }

    public override void CopyTo(Array array, int arrayIndex)
    {
        lock (this._table.SyncRoot)
        {
            this._table.CopyTo(array, arrayIndex);
        }
    }

    public override IDictionaryEnumerator GetEnumerator()
    {
        return this._table.GetEnumerator();
    }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
        {
            throw new ArgumentNullException("info");
        }
        info.AddValue("ParentTable", this._table, typeof(Hashtable));
    }

    public override void OnDeserialization(object sender)
    {
    }

    public override void Remove(object key)
    {
        lock (this._table.SyncRoot)
        {
            this._table.Remove(key);
        }
    }

    internal override KeyValuePairs[] ToKeyValuePairsArray()
    {
        return this._table.ToKeyValuePairsArray();
    }

    // Properties
    public override int Count
    {
        get
        {
            return this._table.Count;
        }
    }

    public override bool IsFixedSize
    {
        get
        {
            return this._table.IsFixedSize;
        }
    }

    public override bool IsReadOnly
    {
        get
        {
            return this._table.IsReadOnly;
        }
    }

    public override bool IsSynchronized
    {
        get
        {
            return true;
        }
    }

    public override object this[object key]
    {
        get
        {
            return this._table[key];
        }
        set
        {
            lock (this._table.SyncRoot)
            {
                this._table[key] = value;
            }
        }
    }

    public override ICollection Keys
    {
        get
        {
            lock (this._table.SyncRoot)
            {
                return this._table.Keys;
            }
        }
    }

    public override object SyncRoot
    {
        get
        {
            return this._table.SyncRoot;
        }
    }

    public override ICollection Values
    {
        get
        {
            lock (this._table.SyncRoot)
            {
                return this._table.Values;
            }
        }
    }
}

 
Collapse Methods
 

呵呵,果然不出我们所料,SyncHashtable果然继承自Hashtable,SyncHashtable之所有能实现线程的安全操作,就是因为在他们的一些方法中,就加了lock,我们知道,哪一个线程执行了lock操作,在他还没有释放lock之前,其他线程都要处于堵塞状态。SyncHashtable就是通过这种方法,来实现所谓的线程安全。

现在我们理解了Synchronized的含义和用法,那接下来我们看看他和SyncRoot之间的关系。
SyncRoot表示获取可用于同步 Hashtable 访问的对象,老实说,这个解释不好理解,要想真正理解他的用法,我们还得从源码开始:

public   virtual   object  SyncRoot
{
    get
    {
        if (this._syncRoot == null)
        {
            Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
        }
        return this._syncRoot;
    }
}
 

 

如果您清楚Interlocked的用法,这段代码没什么难理解的了(不清楚的朋友找GOOGLE吧),Interlocked为多个线程共享的变量提供原子操作。 原子操作就是单线程操作。在一个Hashtable实例中,不论我们在代码的任何位置调用,返回的都是同一个object类型的对象。我们在开始写的lock(ht.SyncRoot)和下边的操作作用是一样的.

static   object  obj  =   new   object ();
lock (obj)
{
   //一些操作
}

他们之间不同的是,我们声明的static object类型对象是类型级别的,而SyncRoot是对象级别的。

通过上面的分析,我们都应该能理解Synchronized 和 SyncRoot用法,他们之间的关系就是:
Hashtable通过Synchronized方法,生成一个SynchHashtable类型的对象,在这个对象的一个方法中,通过lock (this._table.SyncRoot)这样的代码来实现线程安全的操作,其中this._table.SyncRoot返回的就是一个object类型的对象,在一个SynchHashtable对象实例中,不管我们调用多少次,他是唯一的。

在许多的集合类中,都能看到Syncronized静态方法和SyncRoot实例属性,这两个单词的sync就显而易见的说明了是用来实现同步的。集合类通常不是线程安全的,多个读取器可以安全的读取集合.但是对集合的任何修改都将为访问集合的所有线程生成不明确的结果,对于集合类来讲,我们可以使用下面两种方式来避免:

(1) Synchronized 方法,并通过该包装以独占方式访问集合,编译器会自动生成适当的 Monitor.Enter 和 Monitor.Exit 调用

(2) 在访问该集合时对SyncRoot属性使用锁定机制

这两种方式到底有哪些区别的,实际应用时应该使用哪种方法呢?

首先需要明确的是Synchronized 并不能保证枚举的同步,如果没有任何线程在读取 Hashtable,则 Synchronized 支持使用多个写入线程。如果使用一个(或多个)读取器以及一个(或多个)编写器,则同步包装不提供线程安全的访问,也就是说使用这个方法并不能保证枚举的同步,例如,一个线程正在删除或添加集合项,而另一个线程同时进行枚举,这时枚举将会抛出异常。所以,在枚举的时候,你必须明确锁定这个集合。这时就要用到SyncRoot。

什么是SyncRoot呢,可以这样认为,一些集合类自己维护着一个内部的数据结构,而SyncRoot就是这个一个内部对象,如果给了对这个内部数据结构的访问权,那么仅仅锁定集合对象是无用的。此时就要用到SyncLock(ht.SyncRoot)来保证集合的变化。

今天同事告诉我, 锁 hashtable 应该锁它的 SyncRoot 属性而不应该锁它的实例, 例如:

Hashtable ht = new Hashtable();
lock(ht.SyncRoot)
{
...
}

看了 .Net Framework 文档, 给的例子也是锁 SyncRoot 属性, 说如果锁实例的话不能保证在并发情况下的同步, 我很疑惑, 为什么不能锁 hashtable 实例本身呢?

做了个实验, 两个线程 A 和 B, 用锁实例和锁 SyncRoot 两种方式测试, 都没有问题, 结果是一样的。

后来, 用 Hashtable.Synchronized 创建自动线程同步的 hashtable, 终于明白了 SyncRoot 的作用。先说说自动线程同步的 Hashtable: 如果 Hashtable 要允许并发读但只能一个线程写, 要这么创建 Hashtable 实例:

Hashtable hashtable = Hashtable.Synchronized(new Hashtable());

这样, 如果有多个线程并发的企图写 hashtable 里面的 item, 则同一时刻只能有一个线程写, 其余阻塞; 对读的线程则不受影响。

测试的代码是这样的:

Hashtable _hashtable = Hashtable.Synchronized(new Hashtable());

public void TestLock()
{
Thread t1 = new Thread(new ThreadStart(SyncFunctionA));
Thread t2 = new Thread(new ThreadStart(SyncFunctionB));

t1.Start();
t2.Start();

Thread.Sleep(8000);

Console.WriteLine("hashtable[" + _key_a + "] = " + _hashtable[_key_a]);
}

private void SyncFunctionA()
{
lock (_hashtable.SyncRoot)
{
Thread.Sleep(5000);
_hashtable[_key_a] = "Value set by SyncFunctionA";
}
}

private void SyncFunctionB()
{
Console.WriteLine("hashtable[" + _key_a + "] = " + _hashtable[_key_a]);
_hashtable[_key_a] = "Value set by SyncFunctionB";

}

为了清楚的看到效果, 线程 A 用了锁, 并睡眠 5 秒, 睡醒后设置一下 hashtable 里的 item. 线程 B 先读一下 hashtable 里的 item, 再写 hashtable 里的 item。因为对 SyncRoot 加了锁, 即使线程 B 没有显式的对 hashtable 加锁, 但在 _hashtable[_key_a] = "Value set by SyncFunctionB" 一句上也会被 hashtable 自动锁住, 直到线程 A 释放掉 SyncRoot 锁为止。如果线程 A 不是锁 SyncRoot 而是锁 hashtable 实例本身, 那么线程 B 不会在 _hashtable[_key_a] = "Value set by SyncFunctionB" 上被自动锁住。

所以, 总结如下:

如果想锁整个 hashtable, 包括读和写, 即不允许并发的读和写, 那应该锁 hashtable 实例;
如果想允许并发的读, 不允许并发的写, 那应该创建 Synchronized 的 hashtable, 并对要加锁的一块代码用 SyncRoot 锁住, 如果不需要对一块代码加锁, 则 hashtable 会自动对单个写的操作加锁。

Synchronized vs SyncRoot相关推荐

  1. 细细品味C#——Timer及多线程编程

    你对C#中的Timer了解多少?知道有三种Timer吗?他们的区别是什么?编程时选择哪个比较合适? 你会线程(Thread)编程吗?多线程编程又知道多少?断点续传又知多少? 一口气提了这么多问题,其实 ...

  2. 【java线程】锁机制:synchronized、Lock、Condition

    [Java线程]锁机制:synchronized.Lock.Condition 原创 2013年08月14日 17:15:55 标签:Java /多线程 74967 http://www.infoq. ...

  3. java static 可见性_Java多线程 synchronized与可见性的关系以及可见性问题总结

    作者:七里香的编程之路 出自:OSCHINA 原文:my.oschina.net/u/4098550/blog/4548274 能保证可见性的措施 除了volatile 可以让变量保证可见性外.hap ...

  4. 你真的掌握了并发编程volatile synchronized么?

    先看代码: import java.util.concurrent.atomic.AtomicInteger;/**** @author xialuomantian*/ public class Ne ...

  5. Java使用字节码和汇编语言同步分析volatile,synchronized的底层实现

    关于怎么查看字节码的五种方法参考本人另一篇文章<Java以及IDEA下查看字节码的五种方法> 查看汇编语言汇编码 说要看汇编还是很有必要的,因为有些地方比如加锁其实还是通过汇编实现的,只看 ...

  6. java并发vol_java 并发中 volitile、synchronized和lock的比较(一)

    1.volitile和(synchronnized.lock) 首先比较volitile和synchronnized,volitile线程不安全,但是synchronized则是线程安全的. voli ...

  7. synchronized底层原理_你用过synchronized吗?它的底层原理是什么?Java经典面试题来了...

    并发编程已经成为程序员必备技能 作为Java程序员,不懂得并发编程显然已经不能满足市场需求了,尤其是在面试过程中将处于被动地位,也有可能面试将就此终结. 那么作为Java开发者的你,日常虽然可以基于J ...

  8. 面试题-自旋锁,以及jvm对synchronized的优化

    背景 想要弄清楚这些问题,需要弄清楚其他的很多问题. 比如,对象,而对象本身又可以延伸出很多其他的问题. 我们平时不过只是在使用对象而已,怎么使用?就是new 对象.这只是语法层面的使用,相当于会了一 ...

  9. JAVA多线程之Synchronized、wait、notify实例讲解

    一.Synchronized synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争 ...

最新文章

  1. qt LNK2019 无法解析的外部符号
  2. python 创建文件、文件夹、获取目录
  3. 无向图强联通分量-洛谷 P2860 [USACO06JAN]冗余路径Redundant Paths
  4. mysql5.5客户端_MySQL 5.5/5.6——概述 MySQL 客户端程序
  5. TCP TIME_WAIT 详解
  6. 【DevOps】从理念到实施
  7. Android:Android NDK项目中C++文件中打印日志
  8. 在死循环中使用Scanner获得键盘输入
  9. python怎么重复程序_利用Python程序完成ABAQUS中的一些重复性操作
  10. C语言中一个指针传递问题的分析
  11. 多线程编程、线程同步|安全和线程通信
  12. 让Linux脱胎换骨的标致桌面与主题
  13. 台达伺服控制器接线图_【智】两种PLC台达B2伺服驱动器接线和参数设置详解
  14. A Game of Thrones(60)
  15. 微信充错服务器,微信话费充错了怎么追回来(只需一招即可追回)
  16. Maxima 的基本微积分操作
  17. 微软放弃的游戏被他们复活了:Windows经典「三维弹球」现实版,CAD建模、Arduino编程、数控机床打造,硬核致敬童年...
  18. 仿商城商品生成分享海报图片和识别图中二维码并跳转商品详情页
  19. 利用Python进行数据分析
  20. C#之父Anders Hejlsberg演讲解读:编程语言大趋势

热门文章

  1. 中小企业文件服务器搭建
  2. 寻找树中两个结点的最低公共祖先
  3. Android双色球实现
  4. Linux 临时获取root权限
  5. arxiv下载tex模板
  6. Linux 中 reuseport 的演进
  7. 10.幂指函数的极限
  8. 当Web2.0进入企业——免费的企业3.0软件3A完全评测
  9. 休闲娱乐 - 经典儿歌收集
  10. [MMS]彩信MM7_SubmitReq报文