在路上

路上有惊慌,路上有理想

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  28 Posts :: 1 Stories :: 10 Comments :: 0 Trackbacks

一.限制设计——从结构上说,是利用封装技术,保证某一时刻只有一个活动访问某个对象。

方式主要三类,方法限制、线程限制和对象内限制

方法限制:

   1.方法内部限制:采用局部变量方式

   2.方法间传递限制:

         a.调用者copy:比如print(p) 可以改为print(new Point(p));

         b.接收者copy:Point p=new Point(p.x,p.y);

         c.标量参数:print(int x,int y);d.print(p.x,p.y);

线程限制:

     1.最简单的方法是将所有可变对象都放在一个线程内执行

               public display(){

                         new Thread(){

                                  public void run(){//do something here}

                      }.start()

                }

      2.线程私有成员变量

         最直接的办法是利用现有类:ThreadLocal.

        当然你可以利用Thread.currentThread()自己实现一个类似功能的类,但Thread.currentThread有限制,就是对特定线程的一类。

        而ThreadLocal则摆脱了这样的限制。而且在线程内对ThreadLocal私有变量的读写不需要同步。

对象限制

       在前面两种方法都不能做到对对象的限制访问时,你就不得不使用锁。但同时,也可以对对象内部及不同部分的访问进行结构上的限制。

     1.适配器模式

      比如 class Point{

                 public double x;

                 public double y;

                 public synchronized double getX(){};

                //……

      }

    采用对象限制的设计方式,会将synchronized 锁移除到一个其他对象里,这样就解脱了Point.

     like this

           class SychPoint {

                 private final Point point=new Point();

                public synchronized double getX(){point.x}

         }

    class Point{

                 public double x;

                 public double y;

                 public double getX(){};

      }

    说白了就是采用适配器模式,改变了一下原来类的结构。java.util.Collection framework 里面就是使用这种策略组织起集合类的同步。

   2.子类化

       将锁延迟到子类实现,这里将不再罗嗦。

二.同步设计

     使用锁的注意事项

       1.有些入口锁在只有少数线程访问的情况下,可以很好的工作,开销并不大。但是当并发量变大,竞争加剧,开销也变大,系统的性能会随之下降。大多数线程会把大部分时间浪费在等待上。系统出现了延迟,限制了并发系统的优越性。

       2.使用太多的锁,会增加系统负担,以及不可料的情况发生,比如死锁。

       3.只用一把锁来保护一个功能的多个方面会导致资源竞争。

       4.长时间持有锁,会带来性能问题和异常处理的复杂。

       5.有时候加锁并不一定能保证得到我们想要的结果。

    对付以上这些问题,没有什么最佳策略,大都需要去权衡各个方面的利弊来进行设计。写多线程的程序,前期的设计比后期维护更为重要。

    初期的设计原则,

        1.减少同步

             a.用户可以接受陈旧数据,可以拿掉同步,使用volatile

             b.用户在得到非法数据时,只要能得到提示就够了,可以使用double-check方法。

                在不同步时check一次,再在同步状态在check一次。这么做的意义在于缩小锁使用范围,在第一次check不满足的情况,跳出方法,那么锁也就用不到了。

             c.只对维护状态部分加锁:当对象的某个同步方法是比较耗时的操作,那么锁持有的时间就越长,而仅仅是为了保持一个状态是,可以采用openCall的方式,减少持有锁时间。

                              public sychronized void updateState(){}

                              public void readFile(){

                                      updateState();//持有锁

                                    file.read();

                               }

               如上,这种方式的前提是程序不需要同步一个方法中无状态的部分。如果整个方法都需要锁,那这种方式就不适用了.

            D.能使用同步块,就不需同步整个方法。

     2.分解同步:

        分解类

            将锁拆分到辅助类中

        分解锁

           如果不愿分解类,可以设计分解锁

                    private static Object lock1 = new Object();

                   private static Object  lock2 = new Object();

                  synchronize(lock1){}

                  synchronized(lock2){}

              在jdk 5.0之后的并发包里,已有可重入锁供使用。

          隔离成员变量

              Person的age,income等属性都需要同步处理,以保证并发修改时,可以设计一些同步的int,Double等类型(util.concurrent已提供类似的类),将锁交给辅助类去处理。起到隔离作用.

 

posted on 2010-11-11 17:47 阮步兵 阅读(1723) 评论(0)  编辑  收藏 所属分类: Java

只有注册用户登录后才能发表评论。


网站导航: