资讯专栏INFORMATION COLUMN

勿对不可变对象做同步/加锁

wangjuntytl / 299人阅读

摘要:分析从面向对象编程来看,锁状态不应视为不可变对象的一部分,如果对它做同步,就是把锁状态视为它的一部分了,破坏了该对象的设计抽象。从并发编程来看,不可变的对象被设计为允许多线程自由共享,不引起竞争。

另载于 http://www.qingjingjie.com/blogs/10

概念

不可变对象(Immutable Object),就是状态始终不会改变的对象,例如值对象(Value Object),无状态的服务对象(Stateless Service Object)。

Java和Scala都是JVM语言,都经常用synchronized来做同步。本文以Java为例,Scala同理。

先重温一下synchronized的知识:指定了一个同步范围,进出范围刷新变量,并阻止其他线程进入该范围。synchronized method的范围是this,synchronized static method的范围是class,也可显式指定一个对象作为范围。

synchronized(object) {
 ...
}

同步范围是作用于对象的,任何对象都含有一个隐藏的锁状态,JVM把它置为锁态,就加上了当前线程独占的锁。

分析

从面向对象编程来看,锁状态不应视为不可变对象的一部分,如果对它做同步,就是把锁状态视为它的一部分了,破坏了该对象的设计抽象。

从并发编程来看,不可变的对象被设计为允许多线程自由共享,不引起竞争。然而如果对它做同步,就会引起多线程竞争,违反了设计目的。

一般没人会对值对象做同步,但可能有人会误对无状态的服务对象做同步。(牛人也可能有失误)

我们来看个反面例子:

// UserService is singleton
public class UserService {
  // 修改数据库中的用户信息
  public synchronized User changeName(Long id, String name) {
    User user = UserRepo.get(id);
    user.setName(name);
    UserRepo.merge(user);
    return user;
  }
}

通过数据库的事务隔离,能保证user从取出来到存回去之间不被别的线程修改。

但是NoSQL没有事务,怎么办?NoSQL用户可能会用synchronized,这就使得changeName同时只能被一个线程调,网站扛不住并发。

考虑到不同用户的数据可以同时修改,可以给每个用户多带带上锁,以提高并发度:

// UserService is singleton
public class UserService {
  private Map userLocks = new ConcurrentHashMap<>();

  // 修改数据库中的用户信息
  public synchronized User changeName(Long id, String name) {
    // 获取锁
    Object lock = new Object();
    Object prevLock = userLocks.putIfAbsent(id, lock);
    if (prevLock != null) {
      lock = prevLock;
    }
    
    synchronized (lock) {
      try {
        User user = UserRepo.get(id);
        user.setName(name);
        UserRepo.merge(user);
        return user;
      } finally {
        // 防止太多空闲的锁占用内存
        userLocks.remove(id);
      }
    }
  }
}

玩玩而已,这么复杂的代码,我觉得产品里还是不写为好。

况且,在集群环境中,这种单机同步是没用的。

附:JDK也有类似的并发优化,见我的旧文 http://www.cnblogs.com/sorra/p/3653951.html

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64363.html

相关文章

  • 40道阿里巴巴JAVA研发岗多线程面试题详解,你能答出多少

    摘要:但是单核我们还是要应用多线程,就是为了防止阻塞。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。 1、多线程有什么用?一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓知其然知其所以然,会用只是知其然,为什么用才是知其所以然,只有达到知其然知其所以然的程度才可以说是把一个知识点...

    lpjustdoit 评论0 收藏0
  • 多线程基础必要知识点!看了学习多线程事半功倍

    摘要:是需要我们去处理很多事情,为了防止多线程给我们带来的安全和性能的问题下面就来简单总结一下我们需要哪些知识点来解决多线程遇到的问题。 前言 不小心就鸽了几天没有更新了,这个星期回家咯。在学校的日子要努力一点才行! 只有光头才能变强 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 本文章的知识主要参考《Java并发编程实战》这本书的前4章,这本书的前4章都是讲解并发的基...

    YPHP 评论0 收藏0
  • Java 虚拟机总结给面试的你(下)

    摘要:本篇博客主要针对虚拟机的晚期编译优化,内存模型与线程,线程安全与锁优化进行总结,其余部分总结请点击虚拟总结上篇,虚拟机总结中篇。 本篇博客主要针对Java虚拟机的晚期编译优化,Java内存模型与线程,线程安全与锁优化进行总结,其余部分总结请点击Java虚拟总结上篇 ,Java虚拟机总结中篇。 一.晚期运行期优化 即时编译器JIT 即时编译器JIT的作用就是热点代码转换为平台相关的机器码...

    amc 评论0 收藏0
  • 「Java并发编程实战」之对象的共享

    摘要:当某个不应该发布的对象被发布时,这种情况被称为逸出。线程安全共享线程安全的对象在其内部实现同步,因此多线程可以通过对象的公有接口来进行访问而不需要进一步的同步。 前言   本系列博客是对《Java并发编程实战》的一点总结,本篇主要讲解以下几个内容,内容会比较枯燥。可能大家看标题不能能直观的感受出到底什么意思,这就是专业术语,哈哈,解释下,术语(terminology)是在特定学科领域用...

    phodal 评论0 收藏0
  • 线程安全

    摘要:不可变在中,不可变的对象一定是线程安全的。在里标注自己是线程安全的类,大多都不是绝对线程安全,比如某些情况下类在调用端也需要额外的同步措施。无同步方案要保证线程安全,不一定就得需要数据的同步,两者没有因果关系。 在之前学习编程的时候,有一个概念根深蒂固,即程序=算法+数据结构。数据代表问题空间中的客体,代码就用来处理这些数据,这种思维是站在计算机的角度去抽象问题和解决问题,称之为面向过...

    fuyi501 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<