简单的Grails 2.3+服务,用于管理集合,同时考虑多个请求

时间:2014-10-07 22:46:13

标签: grails

我想创建一个通过REST风格的控制器管理集合的服务。我正在考虑为多个人同时打击这项服务所需的保障措施。

所以基本上就是这样......

@Transactional
class NoteService {
    private static users = [:]
    //This won't be so simple it the future
    private static key = 0;
    def get(id) {
        log.debug("We are inside the get")
        return users[id]
    }
    def create(obj){
        log.debug("We are inside the create")
        update(key++, obj)
    }
    def update(id, obj){
        log.debug("We are inside the update")
        users.put(id,obj)
    }
    def delete(id){
        log.debug("We are inside the remove")
        user.remove(id)
    }
}

如果我有多个控制器请求同时命中它,它会工作吗?我担心的是,如果两个客户试图同时点击它,可能会出现问题。也许有更好的策略可能使用承诺?我正在使用2.3 +

2 个答案:

答案 0 :(得分:4)

不,这已经破坏而且不是线程安全的。

如果您没有可变状态,则始终是线程安全的。当然,如果你根本没有状态变量那么它甚至会更好,但是对于依赖注入的Spring bean或记录器之类的东西来说它是很好的。只要你不改变那些值,那么它们就是'实际上是不可变的(它们在启动期间被设置并且之后不会被更改),并且两个并发的调用者不会相互干扰。

但是你确实存在对并发访问最有问题的东西 - 一个状态变量(在这种情况下它没有帮助或伤害它是静态的,因为默认情况下Grails服务是单独的Spring bean,所以map可以是您在多种方法中更改的非静态实例变量并具有相同的问题。

最简单的方法是在地图上进行同步。您不能只是同步方法 - 这只有在访问地图的方法时才有效。使用synchronized将序列化调用并保证不进行并发访问。但是您从多个方法读取和写入,因此序列化对每个方法的调用无助于不同方法的并发调用之间的交互。即使您同步每个方法,您仍然可能偶尔会同时调用两个方法;同步并没有帮助。

所以你需要一种机制来跨方法同步,你在这里有点幸运,因为你只有一个可变字段,所以你可以同步它(当然你总是可以创建一个虚拟'锁'对象和如果您有多个字段被更改,则同步它。然后,所有方法的所有访问(无论它们是否同步,现在你都可以取消同步,因为这只会减慢速度)通过序列化“通过”地图来保护。

这是最简单的,但性能不是很好。如果持有每个同步锁的时间很短,您可能不会注意到很多问题。尝试使同步块尽可能短:

def update(id, obj) {
   log.debug("We are inside the update")

   synchronized(users) {
      users.put(id,obj)
   }
}

更好的解决方案是使用Java 5中添加的java.util.concurrent.*锁定和并发类。如果正确实现,这将非常高效,但是要了解如何使用这些API需要一段时间。最好的资源是Java Concurrency in Practice。它是在2006年编写的,但仍然非常适用(显然它不包括更新的JDK中的更新,但1.5中提供的API以及该书中描述的API对于许多用例来说已足够)。这本书约400页,但材料很难(但很好解释),所以计划在一个多月的时间框架内:)

Venkat Subramaniam的Programming Concurrency on the JVM是另一个很好的资源。它比JCIP更新(2011)并且不那么深入,所以它涵盖的更少但更平易近人。它涵盖了多种JVM语言,包括Groovy。仍然是一个多月的时间框架,但更少的月份。

答案 1 :(得分:0)

这更像是一个“Groovy”呢?我继续使用你推荐的Gpars我不确定我是否正确使用它们:-p。我试过了,似乎工作正常。

import grails.transaction.Transactional
import grails.converters.JSON
import groovyx.gpars.agent.Agent;

@Transactional
class NoteService {
    private static key = 0
    private final  noteState = new Agent<Map<String, String>>([:])
    def get(id) {
        return noteState.val[id]
    }
    def create(obj){
        noteState << { it[(key++).toString()] = obj }
    }
    def update(id, obj){
        noteState << { it[id] = obj }
    }
    def delete(id){
        noteState << { it.remove(id) }
    }
}