Starting/Stopping 一个服务实例在 Java 中安全
Starting/Stopping a service instance safely in Java
我正在开发一个使用 LDAP 服务器作为持久存储的多线程应用程序。我创建了以下服务 class 以在需要时启动和停止 LDAP 服务:
public class LdapServiceImpl implements LdapService {
public void start() {
if (!isRunning()) {
//Initialize LDAP connection pool
}
}
public void stop() {
if (isRunning()) {
//Release LDAP resources
}
}
private boolean isRunning() {
//What should go in here?
}
}
我们目前使用 Google Guice 将服务实现作为单例实例注入:
public class ServiceModule extends AbstractModule {
@Override
protected void configure() {
}
@Provides @Singleton
LdapService providesLdapService() {
return new LdapServiceImpl();
}
}
这样我们就可以在应用程序启动时设置连接池,对连接进行一些操作,然后在应用程序关闭时释放资源:
public static void main(String[] args) throws Exception {
Injector injector = Guice.createInjector(new ServiceModule());
Service ldapService = injector.getInstance(LdapService.class));
ldapService.start();
addShutdownHook(ldapService);
//Use connections
}
private static void addShutdownHook(final LdapService service) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
service.stop();
}
});
}
我面临的问题是我想确保服务只有 started/stopped 一次。出于这个原因,我向服务实现添加了一个 "isRunning()" 方法,但我不确定如何实现它。
考虑到应用程序是多线程的并且我的服务实例是单例,实现 "isRunning()" 方法的最佳方法是什么?
此外,是否有 better/cleaner 实现此目的的方法?
提前致谢。
如果LdapServiceImpl是单例,又担心多个线程并发调用start或stop方法,那么在start和stop方法中加上synchronized关键字即可。到那时,你可以只使用一个简单的布尔标志来存储当前的 运行 状态,只要访问该状态的所有方法都是同步的,你就应该是安全的。
public class LdapServiceImpl implements LdapService {
private boolean isRunning = false;
public synchronized void start() {
if (!isRunning()) {
//Initialize LDAP connection pool
isRunning = true;
}
}
public synchronized void stop() {
if (isRunning()) {
//Release LDAP resources
isRunning = false;
}
}
private boolean isRunning() {
return isRunning;
}
}
djmorton的答案绝对正确,无论是工作分配还是业余项目,您都可以安全地实施它。
话虽如此,这是另一种解决方案 - 有些人可能会争辩说它比安全简单的解决方案有一些优势,但我不会这么说。我添加它只是为了展示另一种方法(并且因为在问题中抛出代码很有趣)。
public static class LdapServiceImpl implements LdapService {
private static final int STOPPED = 0;
private static final int STARTING = 1;
private static final int STOPPING = 2;
private static final int STARTED = 3;
private AtomicInteger serviceState = new AtomicInteger(STOPPED);
public void start() {
if (serviceState.compareAndSet(STOPPED, STARTING)) {
System.out.println("Starting by " + Thread.currentThread().getName());
// Initialize LDAP resources
boolean startSuccess = serviceState.compareAndSet(STARTING, STARTED);
// Handle startSuccess == false, if that somehow happened
}
}
public void stop() {
if (serviceState.compareAndSet(STARTED, STOPPING)) {
System.out.println("Stopping by " + Thread.currentThread().getName());
// Release LDAP resources
boolean stopSuccess = serviceState.compareAndSet(STOPPING, STOPPED);
// Handle stopSuccess == false, if that somehow happened
}
}
}
由于您已经在使用 google 工具,您可以查看 guavas servicemanager http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/ServiceManager.html resp. The service interface http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/Service.html
我正在开发一个使用 LDAP 服务器作为持久存储的多线程应用程序。我创建了以下服务 class 以在需要时启动和停止 LDAP 服务:
public class LdapServiceImpl implements LdapService {
public void start() {
if (!isRunning()) {
//Initialize LDAP connection pool
}
}
public void stop() {
if (isRunning()) {
//Release LDAP resources
}
}
private boolean isRunning() {
//What should go in here?
}
}
我们目前使用 Google Guice 将服务实现作为单例实例注入:
public class ServiceModule extends AbstractModule {
@Override
protected void configure() {
}
@Provides @Singleton
LdapService providesLdapService() {
return new LdapServiceImpl();
}
}
这样我们就可以在应用程序启动时设置连接池,对连接进行一些操作,然后在应用程序关闭时释放资源:
public static void main(String[] args) throws Exception {
Injector injector = Guice.createInjector(new ServiceModule());
Service ldapService = injector.getInstance(LdapService.class));
ldapService.start();
addShutdownHook(ldapService);
//Use connections
}
private static void addShutdownHook(final LdapService service) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
service.stop();
}
});
}
我面临的问题是我想确保服务只有 started/stopped 一次。出于这个原因,我向服务实现添加了一个 "isRunning()" 方法,但我不确定如何实现它。
考虑到应用程序是多线程的并且我的服务实例是单例,实现 "isRunning()" 方法的最佳方法是什么?
此外,是否有 better/cleaner 实现此目的的方法?
提前致谢。
如果LdapServiceImpl是单例,又担心多个线程并发调用start或stop方法,那么在start和stop方法中加上synchronized关键字即可。到那时,你可以只使用一个简单的布尔标志来存储当前的 运行 状态,只要访问该状态的所有方法都是同步的,你就应该是安全的。
public class LdapServiceImpl implements LdapService {
private boolean isRunning = false;
public synchronized void start() {
if (!isRunning()) {
//Initialize LDAP connection pool
isRunning = true;
}
}
public synchronized void stop() {
if (isRunning()) {
//Release LDAP resources
isRunning = false;
}
}
private boolean isRunning() {
return isRunning;
}
}
djmorton的答案绝对正确,无论是工作分配还是业余项目,您都可以安全地实施它。
话虽如此,这是另一种解决方案 - 有些人可能会争辩说它比安全简单的解决方案有一些优势,但我不会这么说。我添加它只是为了展示另一种方法(并且因为在问题中抛出代码很有趣)。
public static class LdapServiceImpl implements LdapService {
private static final int STOPPED = 0;
private static final int STARTING = 1;
private static final int STOPPING = 2;
private static final int STARTED = 3;
private AtomicInteger serviceState = new AtomicInteger(STOPPED);
public void start() {
if (serviceState.compareAndSet(STOPPED, STARTING)) {
System.out.println("Starting by " + Thread.currentThread().getName());
// Initialize LDAP resources
boolean startSuccess = serviceState.compareAndSet(STARTING, STARTED);
// Handle startSuccess == false, if that somehow happened
}
}
public void stop() {
if (serviceState.compareAndSet(STARTED, STOPPING)) {
System.out.println("Stopping by " + Thread.currentThread().getName());
// Release LDAP resources
boolean stopSuccess = serviceState.compareAndSet(STOPPING, STOPPED);
// Handle stopSuccess == false, if that somehow happened
}
}
}
由于您已经在使用 google 工具,您可以查看 guavas servicemanager http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/ServiceManager.html resp. The service interface http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/Service.html