`
frank-liu
  • 浏览: 1669170 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java concurrency: synchronized collections

 
阅读更多

对数据同步访问封装的策略

        我们经常操作一些本身不是线程安全的对象时,在多线程的环境下就会考虑到要采取一些措施来处理。一些典型比如说用synchronized来同步,有的如果情况允许的话使用ThreadLocal变量,还有的会将对象变成immutable的方式。当然,每种方法都需要根据具体问题来分析,不能在保证高并发性和线程安全的情况下完全通用。

 

同步的数据结构

        有一个比较常见的用法就是当我们要访问一些集合类的时候。大多数的集合类比如ArrayList, HashMap之类的都不是线程安全的。为了保证线程安全,我们可能需要用到前面提到的一些同步策略。常见的一种如下:

 

 

public class PersonSet {
    private final Set<Person> mySet = new HashSet<Person>();
    
    public synchronized void addPerson(Person p) {
        mySet.add(p);
    }

    public synchronized boolean containsPerson(Person p) {
        return mySet.contains(p);
    }
}

         这种方法用到一个比较好的思路,就是将一些我们需要多线程访问的数据封装起来。然后对他们的访问操作进行线程同步。这样我们要访问修改的地方就在这个被封装的地方,也方便维护。

        从这种用法我们看到的思路就是封装线程不安全的数据结构,通过这个封装对象添加一些同步的机制来保证最终访问的线程安全。那么,这种思路还有没有其他应用的示例呢?在java中间有一些典型的类,如Collections.synchronizedList, Collections.synchronizedSet等。他们就是用到了这样的思路。

设计思路

        一个典型的synchronizedList用法如下:

 

 

List<Person> persons = new ArrayList<Person>();

List<Person> securePersons = Collections.synchronizedList(persons);

    通过将我们线程不安全的数据传入synchronizedList方法,返回的还是一个list,不过对它的访问就变成线程安全的了。比较有意思吧?通过前面提到的将数据封装起来,然后提供同步机制,我们也就可以猜到,这种方式也是通过同样的手法。

 

        现在,如果我们足够好奇的话,再进一步想想。最前面那个示例对数据进行了封装,再次访问这个arrayList的时候,实际上是通过访问封装对象提供的方法来使用的。而我们这边进行了封装之后居然和可以把它当成一个List来用。那么,这说明了什么呢?说明我们这个封装它的类肯定提供了和List一样的接口。如果我们对某些设计模式比较熟悉的话,再回想这么一句,实现相同的接口,封装了另外一个对象,对这么一个对象增加了某些功能,但是还能当成同样接口的对象用。

       呵呵,像什么呢?好像很熟悉的感觉,有点decorator pattern的感觉,也有点像adapter。

       如果我们深入代码的细节去探索的话,会发现它实际的实现方式是用到了decorator pattern。代码本身并不负责,具体的代码细节就不去赘述了。他们的类结构关系如下图:

        这是一个稍微复杂一点的decorator pattern实现,每个进行封装的类如SynchronizedList, SynchronizedSet都继承自SynchronizedCollection,其他的需要被封装的类如ArrayList, LinkedList和SynchronizedCollection实现同样的接口。我们再对照一下典型的decorator pattern类图结构:

 

 

 

        这里头,decorator就相当于一个二道贩子。它把一些现成的需要封装的对象拿过来,然后按照同样的规格(接口)在折腾出一个具有一点新功能的对象。想到这里,一种山寨的感觉油然而生。

总结

        我们为了提供一个线程安全的数据结构而采用了一种封装对象。这样,用户就可以很方便的使用一个封装调用而得到所期望的同步效果。这样比直接去加synchronized要方便多了,更加不容易出错。在这么一个简单易用的外表下面,还是隐藏着一些封装的复杂手法。唉,扮靓是需要代价的。

参考资料

java concurrency in practice

openjdk

  • 大小: 71.5 KB
分享到:
评论
3 楼 xiaolong8 2014-11-06  
frank-liu 写道
xiaolong8 写道
装饰模式是“新增行为”,而代理模式是“控制访问”。线程同步访问属于控制访问。

所以我觉得应该是代理模式。

我觉得它这是用装饰模式的样式实现了代理的行为,从访问控制的角度来说,它们两者的界限很模糊。



后来我想想还是是属于装饰模式,因为代理模式的控制访问能禁止,而这里只是让各线程串行访问执行,并没有去真正的控制访问。而是对访问方式作了串行处理,可以理解为一个对访问方式的修饰。
2 楼 frank-liu 2014-11-01  
xiaolong8 写道
装饰模式是“新增行为”,而代理模式是“控制访问”。线程同步访问属于控制访问。

所以我觉得应该是代理模式。

我觉得它这是用装饰模式的样式实现了代理的行为,从访问控制的角度来说,它们两者的界限很模糊。
1 楼 xiaolong8 2014-10-31  
装饰模式是“新增行为”,而代理模式是“控制访问”。线程同步访问属于控制访问。

所以我觉得应该是代理模式。

相关推荐

Global site tag (gtag.js) - Google Analytics