• magict4
    2019-03-26
    1. setUpper() 跟 setLower() 都加上 "synchronized" 关键字。不要太在意性能,老师都说了,避免过早优化。
    2. 如果性能有问题,可以把 lower 跟 upper 两个变量封装到一个类中,例如
    ```
    public class Boundary {
        private final lower;
        private final upper;
        
        public Boundary(long lower, long upper) {
            if(lower >= upper) {
                // throw exception
            }
            this.lower = lower;
            this.upper = upper;
        }
    }
    ```
    移除 SafeVM 的 setUpper() 跟 setLower() 方法,并增入 setBoundary(Boundary boundary) 方法。
    展开

    作者回复: 👍👍

     4
     62
  • 海鸿
    2019-03-26
    我看有些人评论用volatie,volatie只能保证可见性,但是保证不了原子性,所以得加锁保证互斥。
    老师我这样理解对吗?

    作者回复: 相当正确!

    
     19
  • QQ怪
    2019-03-27
    必须加锁啊,synchronized (this)就行了,最简单加锁吧,volatile只能保证内存可见性,并不能保证原子性

    作者回复: 👍

    
     9
  • Boomkeeper
    2019-04-09
    我就不明白了,使用了synchronized,为啥还用voliate,他的确是保证可见性,但是并不能保证原子性,一般他的应用场景应该是不依赖之前的结果而改变数据,累加的场景明显不适合
     1
     5
  • Cc
    2019-03-29
    又想到一种,既然两个变量要同时锁定,那就把两个变量封装成一个,然后使用cas操作。这样行不行,另外老师帮我看看volatile是不是有多余的地方
    ···········
    volatile AtomicReference<Inventory> inventory = new AtomicReference<>();

        static class Inventory {
            private volatile long upper = 0;

            private volatile long lower = 0;
        }

        void setUpper(long v) {
            long low;
            Inventory oldObj;
            Inventory newObj;
            do {
                oldObj = inventory.get();
                if (v >= (low = oldObj.lower)) {
                    throw new IllegalArgumentException();
                }
                newObj = new Inventory();
                newObj.lower = low;
                newObj.upper = v;

            } while (inventory.compareAndSet(oldObj, newObj));
        }

        void setLower(long v) {
            long upp;
            Inventory oldObj;
            Inventory newObj;
            do {
                oldObj = inventory.get();
                if (v <= (upp = oldObj.upper)) {
                    throw new IllegalArgumentException();
                }
                newObj = new Inventory();
                newObj.lower = v;
                newObj.upper = upp;

            } while (inventory.compareAndSet(oldObj, newObj));
        }


    展开

    作者回复: 我觉得这个没有问题,volatile 换成 final会更好

     1
     5
  • 木木匠
    2019-03-26
    思考题:对于两个互相比较的变量来说,赋值的时候只能加锁来控制。但是这也会带来性能问题,不过可以采用读锁和写锁来优化,申请写锁了就互斥,读锁可以并发访问,这样性能相对粗粒度的锁来说会高点。

    作者回复: 👍

    
     3
  • 抽离の❤️
    2019-03-26
    老师、讲的真好!
    
     3
  • 阿琨
    2019-03-26
    老师,这样写有什么问题吗,总感觉哪里怪怪的。
    public class DBPush {
        private volatile static DBPush dbPush = null;

        private DBPush() {
        }

        public static DBPush getInStance() {
            if (dbPush == null) {
                synchronized (DBPush.class) {
                    if (dbPush == null) {
                        dbPush = new DBPush();
                    }
                }
            }
            return dbPush;
        }
    }
    展开

    作者回复: 没问题

    
     2
  • 悟空
    2019-03-26
    访问时使用syncchronize对类加锁。保证变量访问的互斥

    作者回复: 对象加锁就可以了

    
     2
  • 江南豆沙包
    2019-03-26
    令哥,一直在追你的专栏,结合公司目前实际情况,有个疑问,如果你专栏中的例子中的共享信息,是整个系统维度的,系统又是多实例集群部署的,我们该怎么办呢,能不能在思想或实现思路上给点建议指导。

    作者回复: 感谢信任!我们这里说的共享是进程级别的,如果是分布式计算只能靠redis,db,zk这些来搞分布式的锁,当然不共享是最好的解决方案。

    
     2
  • Lemon
    2019-03-26
    使用 Condition
    public class SafeWM {

        // 库存上限
        private final AtomicLong upper =
                new AtomicLong(10);
        // 库存下限
        private final AtomicLong lower =
                new AtomicLong(2);

        private ReentrantLock lock = new ReentrantLock();
        private Condition c1 = lock.newCondition();
        private Condition c2 = lock.newCondition();
        
        // 设置库存上限
        void setUpper(long v) {
            try {
                lock.lock();
                // 检查参数合法性
                while (v < lower.get()) {
                    c1.await();
                }
                upper.set(v);
                c2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }

        // 设置库存下限
        void setLower(long v) {

            try {
                lock.lock();
                // 检查参数合法性
                while (v > upper.get()) {
                    c2.await();
                }
                lower.set(v);
                c1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    }
    展开

    作者回复: 错误的设置会导致永远等待,太危险了

    
     2
  • Zm
    2019-03-26
    Volatile修饰变量
    
     2
  • 无庸
    2019-03-26
    compareAndSet吧

    void setUpper(long v){
      upper.compareAndSet(upper.longValue
    (),v);
    }
    展开

    作者回复: 不满足合理库存的约束条件

    
     2
  • 京
    2019-03-26
    一早起来,就把文章看完了,期待老师后面更精彩的内容
    
     2
  • 花花young
    2019-11-30
    validate我看书中说像a=4这种是有原子性的,对于复合操作是没有原子性,望老师解答

    作者回复: 是这样的,可以使用JDK提供的原子类

    
     1
  • 拯救地球好累
    2019-07-24
    ---总结---
    1. 封装与并发:将共享变量作为对象属性封装在内部,对所有公共方法指定并发访问策略
    2. 不可变与并发:如无必要修改,实例变量都应该是private final的
    3. 共享变量间的约束条件决定了并发访问策略,而为了达到这种约束条件,基本上都会有if语句,此时往往会出现竞态条件
    4. 并发访问策略:避免共享;不变模式;同步工具

    ---启发---
    1. 经常能将计算机的两个概念放在一起,看看两者的关联,会有不同的收获。
    展开
    
     1
  • 逆水行舟
    2019-03-31
    那本书,有些晦涩,但是是必读的。

    作者回复: 太晦涩了

     1
     1
  • zero
    2019-03-27
    public class SafeWM {
      // 库存上限
      private final AtomicLong upper =
            new AtomicLong(0);
      // 库存下限
      private final AtomicLong lower =
            new AtomicLong(0);
      // 设置库存上限
      void setUpper(long v) {
        synchronized (this) {
          // 检查参数合法性
          if (v < lower.get()) {
            throw new IllegalArgumentException();
          }
          upper.set(v);
        }
      }
      // 设置库存下限
      void setLower(long v) {
        synchronized (this) {
          // 检查参数合法性
          if (v > upper.get()) {
            throw new IllegalArgumentException();
          }
          lower.set(v);
        }
      }
      // 省略其他业务代码
    }

    老师,这样处理可以吗?
    展开

    作者回复: 可以

    
     1
  • 我是卖报小行家
    2019-03-27
    前面有个朋友说final保证不会逃逸,理解应该有误,fianl的禁止重排序前提是构造函数里面没有this逃逸,他只保证final变量不会重排序到构造函数之外。并不保证逃逸。

    作者回复: 👍

    
     1
  • crazypokerk
    2019-03-26
    我觉得对于上限与下限不一致的问题,主要是因为出现了竞态条件,而解决竞态条件与数据竞争的解决办法就是互斥,即加锁。只有当一个线程获得锁之后,判断条件是否成立之后,再进行更改操作。如理解有误,还请老师指正。

    作者回复: 对的

    
     1
我们在线,来聊聊吧