当前位置: 首页 > 知识库问答 >
问题:

单例类公共方法是否应该同步?

楚德辉
2023-03-14
public class ElasticSearchClient {    

    private static volatile ElasticSearchClient elasticSearchClientInstance;

    private static final Object lock = new Object();

    private static elasticConfig ; 

    /*
    ** Private constructor to make this class singleton
    */
    private ElasticSearchClient() {
    }

    /*
    ** This method does a lazy initialization and returns the singleton instance of ElasticSearchClient
    */
    public static ElasticSearchClient getInstance() {
        ElasticSearchClient elasticSearchClientInstanceToReturn = elasticSearchClientInstance;
        if (elasticSearchClientInstanceToReturn == null) {
            synchronized(lock) {
                elasticSearchClientInstanceToReturn = elasticSearchClientInstance;
                if (elasticSearchClientInstanceToReturn == null) {
                    // While this thread was waiting for the lock, another thread may have instantiated the clinet.
                    elasticSearchClientInstanceToReturn = new ElasticSearchClient();
                    elasticSearchClientInstance = elasticSearchClientInstanceToReturn;
                }
            }
        }
        return elasticSearchClientInstanceToReturn;
    }

    /*
    ** This method creates a new elastic index with the name as the paramater, if if does not already exists.
    *  Returns true if the index creation is successful, false otherwise.
     */
    public boolean createElasticIndex(String index) {
        if (checkIfElasticSearchIndexExists(index)) {
            LOG.error("Cannot recreate already existing index: " + index);
            return false;
        }
        if (elasticConfig == null || elasticConfig.equals(BatchConstants.EMPTY_STRING)) {
            loadElasticConfigFromFile(ELASTIC_CONFIG_FILE_NAME);
        }
        if (elasticConfig != null && !elasticConfig.equals("")) {
            try {
                HttpURLConnection elasticSearchHttpURLConnection = performHttpRequest(
                    ELASTIC_SEARCH_URL + "/" + index,
                    "PUT",
                    elasticConfig,
                    "Create index: " + index
                );

                return elasticSearchHttpURLConnection != null &&
                       elasticSearchHttpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK;
            } catch (Exception e) {
             LOG.error("Unable to access Elastic Search API. Following exception occurred:\n" + e.getMessage());
          }
        } else {
            LOG.error("Found empty config file");
        }
        return false;
    }

private void loadElasticConfigFromFile(String filename) {

    try {
        Object obj = jsonParser.parse(new FileReader(filename);
        JSONObject jsonObject = (JSONObject) obj;
        LOG.info("Successfully parsed elastic config file: "+ filename);
        elasticConfig = jsonObject.toString();
        return;
    } catch (Exception e) {
        LOG.error("Cannot read elastic config from  " + filename + "\n" + e.getMessage());
        elasticConfig = "";
    }
}

}

我有多个线程使用ElasticSearchClient,如下所述

Thread1
ElasticSearchClient elasticSearchClient = ElasticSearchClient.getInstance()
elasticSearchClient.createElasticIndex("firstindex");

Thread2
ElasticSearchClient elasticSearchClient = ElasticSearchClient.getInstance()
elasticSearchClient.createElasticIndex("secondindex");

Thread3...

据我所知,Singleton类是线程安全的,但我不确定如果多个线程开始执行Singleton类的同一方法会发生什么。这有副作用吗?

注意:我知道上面的singleton类不是反射和序列化安全的。

共有1个答案

富涛
2023-03-14

在您的特定实现中

if (checkIfElasticSearchIndexExists(index)) { //NOT THREAD SAFE
            LOG.error("Cannot recreate already existing index: " + index);
            return false;
        }
        if (elasticConfig == null || elasticConfig.equals(BatchConstants.EMPTY_STRING)) { //NOT THREAD SAFE
            loadElasticConfigFromFile(ELASTIC_CONFIG_FILE_NAME);
        }
        if (elasticConfig != null && !elasticConfig.equals("")) { //NOT THREAD SAFE

有3点可能导致赛车状况。

就其本身而言

单例类公共方法是否应该同步?

没有这样的规则——如果这些规则是线程安全的,那么就不需要额外的同步。在你的情况下,这些不是线程安全的,因此你必须使它们是安全的

public synchronized boolean createElasticIndex

如果您担心并发写入单个索引,那么就不要这样做-正确处理并发写入是一项弹性搜索任务(相信ES会顺利处理)

什么是线程不安全的(指出3处)?同时具有T1和T2:

  1. checkifelasticsearchindexists(index)如果T1和T2使用相同的索引名,则两者都将通过此检查(我仅假设这是一个rest调用-这甚至是最糟糕的)

2和3可以通过双重检查锁定来修复(就像您在getInstance()中所做的那样),或者我更愿意将其移动到实例初始化块,我认为构造函数是最好的方法。

为了更好地理解这种现象,你可以检查为什么a==1

 类似资料:
  • 问题内容: 我没有使用Spring,所以正在类中创建EntityManager的实例。 我使用了Hibernate-Eclipse逆向工程来自动生成类。这些类都有一个EntityManager的实例。 我不确定100%Hibernate如何与EntityManager一起工作,所以我想知道是否可以创建这么多的此类实例(EntityManager),例如,事务是否会出现问题? 我是否应该仅创建一个单

  • 当main方法在类QNA中时程序运行良好,但在类中定义时给出错误测试错误:在类QNA中找不到main方法,请将main方法定义为:public static void main(String[]args)或JavaFX应用程序类必须扩展JavaFX.application.application

  • 简介 框架中内置封装了一些公共函数,开发者在实际业务中可以直接使用,无需重复封装。其中包括: 协程函数 数组函数 目录(文件夹)函数 环境函数 文件函数 文件系统函数 对象函数 PHP 助手函数 字符串函数 系统函数 XML 函数 通用函数 协程函数 创建协程Swoft 框架中不能使用 Swoole 提供的 go 函数创建协程,否则会造成请求和上下文丢失最终导致一些不可预估的问题。 Swoft 拥

  • 我从A类中创建了三个对象。所有这三个对象都可以更新存储在A类私有静态易失性变量中的值。更新该变量是在具有特定条件的同步块中完成的。我想通过使用锁对象来同步块。 因此,首先在MainClass中创建对象 在这之后,物体开始过自己的生活。这是我的a班的一个简化例子。 如果我希望同步块与类A的所有实例和所有线程同步,我应该将lockObject声明为私有静态易失性吗?如果我使用类(this)来同步块,它

  • 问题内容: 我正在编写一个将异步检索数据的服务($ http或$ resource)。我可以通过返回一个最初为空但最终将填充的数组来隐藏它是异步的事实: 或者我可以通过返回一个承诺来暴露异步性: (Plunker-如果有人想尝试上述两种实现。) 暴露异步性的一个潜在优势是,我可以通过向方法中添加错误处理程序来处理控制器中的错误。但是,我很可能会在应用程序范围的拦截器中捕获并处理$ http错误。