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的所有方法,并且您必须根据需要保护其状态。 这可以通过使用sync和volatile来完成。 但是要小心,很多时候这并不像看起来那样容易。
容器管理并发
容器托管并发性更易于使用,但不如Bean托管方法灵活。 但是根据我的经验,对于一般用例来说已经足够了。
正如我们在日志中看到的那样,容器管理的并发性是EJB Singleton的默认值。 容器为整个Singleton设置写锁,并序列化所有方法调用。
我们可以更改此行为,并在方法和/或类级别上定义读写锁定。 这可以通过使用@ javax.ejb.Lock(javax.ejb.LockType)注释Singleton类或方法来完成。 LockType枚举提供值WRITE和READ来定义互斥写锁定或读锁定。
以下代码段显示了如何将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
singleton模式