有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程在Java中安全启动/停止服务实例

我正在开发一个使用LDAP服务器作为持久存储的多线程应用程序。我创建了以下服务类,用于在需要时启动和停止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();
        }
    });
}

我面临的问题是,我想确保服务只启动/停止一次。出于这个原因,我在服务实现中添加了一个“isRunning()”方法,但我不确定如何实现它

考虑到应用程序是多线程的,而我的服务实例是单实例,实现“isRunning()”方法的最佳方法是什么

此外,是否有更好/更清洁的方法来实现这一点

提前谢谢


共 (3) 个答案

  1. # 1 楼答案

    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
          }
       }
    
    }
    
  2. # 2 楼答案

    如果LdapServiceImpl是单例的,并且您担心多个线程同时调用start或stop方法,那么您应该能够简单地将synchronized关键字添加到start和stop方法中。此时,您可以只使用一个简单的布尔标志来存储当前的运行状态,只要访问该状态的所有方法都是同步的,您就应该是安全的

    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;
        }
    }