Java EE陷阱#1:忽略@Singleton的默认锁定

EJB Singleton Bean是EJB 3.1规范引入的,通常用于存储缓存的数据。 这意味着,我们尝试通过使用Singleton来提高应用程序的性能。 总的来说,这很好。 特别是在并行调用不多的情况下。 但是,如果我们忽略默认锁,并且并行调用的数量增加,它就会改变。

合理的默认值

让我们从一些Java代码开始,看看如何合理设置锁。 以下代码片段显示了一个简单的带有计数器和两个方法的EJB Singleton。 method1将计数器的当前值写入日志,method2的计数从0到100。

@Singleton
@Remote(SingletonRemote.class)
public class DefaultLock implements SingletonRemote {
    Logger logger = Logger.getLogger(DefaultLock.class.getName());

    private int counter = 0;

    @Override
    public void method1() {
        this.logger.info("method1: " + counter);
    }

    @Override
    public void method2() throws Exception {
        this.logger.info("start method2");
        for (int i = 0; i < 100; i++) {
            counter++;
            logger.info("" + counter);
        }
        this.logger.info("end method2");
    }
}

如您所见,没有定义锁。 如果我们同时调用两个方法,您希望在日志文件中看到什么?

2014-06-24 21:18:51,948 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 5) method1: 0
2014-06-24 21:18:51,949 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) start method2
2014-06-24 21:18:51,949 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 1
2014-06-24 21:18:51,949 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 2
2014-06-24 21:18:51,950 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 3
  ...
2014-06-24 21:18:51,977 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 99
2014-06-24 21:18:51,977 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 100
2014-06-24 21:18:51,978 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) end method2
2014-06-24 21:18:51,978 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 6) method1: 100
2014-06-24 21:18:51,981 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 7) method1: 100
2014-06-24 21:18:51,985 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 8) method1: 100
2014-06-24 21:18:51,988 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 9) method1: 100

好的,这可能有点意外,默认是整个Singleton上的容器管理的写锁定。 这是一个很好的默认设置,以避免同时修改属性。 但是,如果我们要执行只读操作,那么这是一个糟糕的默认设置。 在这种情况下,方法调用的序列化将导致高负载下较低的可伸缩性和较低的性能。

如何避免呢?

这个问题的答案很明显,我们需要注意并发管理。 和Java EE中一样,有两种方法可以处理它。 我们可以自己做,也可以要求容器做。

Bean托管并发

我不想过多地讨论Bean管理的并发性。 这是管理并发访问的最灵活的方法。 容器允许并发访问Singleton的所有方法,并且您必须根据需要保护其状态。 这可以通过使用syncvolatile来完成。 但是要小心,很多时候这并不像看起来那样容易。

容器管理并发

容器托管并发性更易于使用,但不如Bean托管方法灵活。 但是根据我的经验,对于一般用例来说已经足够了。

正如我们在日志中看到的那样,容器管理的并发性是EJB Singleton的默认值。 容器为整个Singleton设置写锁定,并序列化所有方法调用。

我们可以更改此行为,并在方法和/或类级别上定义读写锁。 这可以通过使用@ javax.ejb.Lock(javax.ejb.LockType)注释Singleton类或方法来完成。 LockType枚举提供值WRITEREAD来定义互斥写锁定或读锁定。

以下代码片段显示了如何将method1和method2的Lock设置为LockType.READ

@Singleton
@Remote(SingletonRemote.class)
public class ReadLock implements SingletonRemote {
    Logger logger = Logger.getLogger(ReadLock.class.getName());

    private int counter = 0;

    @Override
    @Lock(LockType.READ)
    public void method1() {
        this.logger.info("method1: " + counter);
    }

    @Override
    @Lock(LockType.READ)
    public void method2() throws Exception {
        this.logger.info("start method2");
        for (int i = 0; i < 100; i++) {
            counter++;
            logger.info("" + counter);
        }
        this.logger.info("end method2");
    }
}

如前所述,我们可以通过使用@Lock(LockType.READ)注释类而不是同时使用这两种方法来实现相同的目的。

好的,如果一切都按预期进行,则应该并行访问这两种方法。 因此,让我们看一下日志文件。

2014-06-24 21:47:13,290 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 10) method1: 0
2014-06-24 21:47:13,291 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) start method2
2014-06-24 21:47:13,291 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 1
2014-06-24 21:47:13,291 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 2
2014-06-24 21:47:13,291 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 3
   ...
2014-06-24 21:47:13,306 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 68
2014-06-24 21:47:13,307 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 69
2014-06-24 21:47:13,308 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 3) method1: 69
2014-06-24 21:47:13,310 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 70
2014-06-24 21:47:13,310 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 71
   ...
2014-06-24 21:47:13,311 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 76
2014-06-24 21:47:13,311 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 77
2014-06-24 21:47:13,312 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 2) method1: 77
2014-06-24 21:47:13,312 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 78
2014-06-24 21:47:13,312 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 79
   ...
2014-06-24 21:47:13,313 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 83
2014-06-24 21:47:13,313 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 84
2014-06-24 21:47:13,314 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 5) method1: 84
2014-06-24 21:47:13,316 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 85
2014-06-24 21:47:13,316 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 86
2014-06-24 21:47:13,317 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 87
2014-06-24 21:47:13,318 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 88
2014-06-24 21:47:13,318 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 6) method1: 89
2014-06-24 21:47:13,318 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 89
2014-06-24 21:47:13,319 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 90
   ...
2014-06-24 21:47:13,321 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 99
2014-06-24 21:47:13,321 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 100
2014-06-24 21:47:13,321 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) end method2

结论

在本文开头,我们发现Java EE使用容器管理的写锁作为默认值。 这导致所有方法调用的序列化处理,并降低了应用程序的可伸缩性和性能。 在实现EJB Singleton时,我们需要牢记这一点。

我们看了两个用于控制并发管理的现有选项:Bean管理的并发和容器管理的并发。

我们使用容器托管方法为单例的这两种方法定义了一个读锁。 这不像bean管理的方法那样灵活,但是它更容易使用,并且在大多数情况下足够了。 我们只需要提供一个注释,容器将处理其余的注释。

翻译自: https://www.javacodegeeks.com/2014/06/java-ee-pitfalls-1-ignore-the-default-lock-of-a-singleton.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值