使用同步块预订座位

时间:2015-12-30 10:46:41

标签: java multithreading grails concurrency gorm

我正在尝试使用这样的同步块实现座位预订验证:

synchronized(this) {
    int seatsBooked = GrantAssessment.countByExamSessionAndExamDateBetweenAndIsCancelled(examSession,now,now+1,false)
    int seatsRemaining = examSession.maxSeat - seatsBooked
    if(seatsRemaining<1){
        throw new CustomValidationException("All seats booked...")
    }

    // assign assessment which increases countByExam... query count by 1
    grantAssessment = assignAssessment(examCommerce,examSession,examDate,identificationType,idNumber)
}

assignAssessment()方法代码如下:

def assignAssessment(ExamCommerce examCommerce, ExamSession examSession,Date examDate,IdentificationType identificationType,String idNumber) {
    .................
    examSession.addToGrantAssessmentList(grantAssessment)
    ..............................
    grantAssessment.save(failOnError: true,flush: true)
    examSession.save(failOnError: true,flush: true)
    return grantAssessment
}

当我使用浏览器1(不同的线程)命中时,它进入同步块并分配一个座位。当浏览器2(线程2,几乎相同的时间)进入块时,下面的代码返回的查询计数:

     GrantAssessment.countByExamSessionAndExamDateBetweenAndIsCancelled(examSession,now,now+1,false)

是相同的。但是同一个线程在同步块之后显示减少的值(正确)。

因此,即使totalSeat等于1,两个线程都会分配座位。

如何处理并发性,以便以同步方式正确计算availableSeats值。 JMS可以用于这种情况吗?

1 个答案:

答案 0 :(得分:2)

好像你正在使用不同的显示器。 例如,以下内容可能会导致与您描述的both the threads assigns the seat even if totalSeat is equal to 1.

相同的状态
private static ExecutorService executorService = Executors.newFixedThreadPool(2);

boolean bookTwoSeatsInParallel() {
    Future<Integer> res1 = executorService.submit(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            synchronized(this) {
                //your seat booking code which returns seat num or whatever
            }
            return -1;
        }
    });

    Future<Integer> res2 = executorService.submit(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            synchronized(this) {
                //your seat booking code which returns seat num or whatever
            }
            return -1;
        }
    });
}

res1.get().equals(res2.get())可能是真的。此示例有点多余,但它显示了不同线程在尝试使用不同监视器以实现有效同步状态时的情况。
要解决此问题,您应该在同一台显示器上进行同步,例如

private static ExecutorService executorService = Executors.newFixedThreadPool(2);
private final Object bookingMonitor = new Object();

boolean bookTwoSeatsInParallel() {
    Future<Integer> res1 = executorService.submit(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            synchronized(bookingMonitor) {
                //your seat booking code which returns seat num or whatever
            }
            return -1;
        }
    });

    Future<Integer> res2 = executorService.submit(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            synchronized(bookingMonitor) {
                //your seat booking code which returns seat num or whatever
            }
            return -1;
        }
    });
}

请注意,在synchronized(this) {...}块内读取/修改的所有变量不应在其他位置读取/修改,而​​不能在同一监视器上同步。在其他情况下,它可以导致Thread InterferenceMemory Consistency Errors

  

对于这种情况,JMS是否正常?

当然,您可以使用JMS,通过它将预订请求传递给唯一的工作线程。但是对于这个简单的案例,你不需要这么复杂的解决方案。