8

我有一堂课:

class PrintStringDataBuilder
{
    PrintStringDataBuilder() { }
    public static GetInstance()
    {
        return new PrintStringDataBuilder();
    }

    //other class methods and fields, properties
}

从客户端代码访问为:

PrintStringDataBuilder instance = PrintStringDataBuilder.GetInstance();

上面的调用是线程安全的吗?

编辑:只是想避免写 PrintStringDataBuilder builder = new PrintStringDataBuilder(); 在 asp.net mvc web 应用程序中多次。PrintStringDataBuilder 类中没有其他静态方法、静态字段或静态属性。

4

4 回答 4

11

是的?在不了解该类构造函数的内部结构的情况下,您可以说调用GetInstance()是线程安全的。但是,该实例上的任何方法都不能保证是线程安全的,特别是因为您没有提供任何这些方法。

这简称为工厂模式。

编辑:如果你想返回一个单例,你可以这样做:

.NET 4+

private static Lazy<PrintStringDataBuilder> _instance = new Lazy<PrintStringDataBuilder>(() =>
  {
      return new PrintStringDataBuilder();
  });

public static PrintStringDataBuilder GetInstance()
{
    return _instance.Value;
}

.NET 3.5 及以下

private static PrintStringDataBuilder _instance = null;
private static object _lockObject = new object();

public static PrintStringDataBuilder GetInstance()
{
    if(_instance == null)
    {
         lock(_lockObject)
         {
              if(_instance == null)
                 _instance = new PrintStringDataBuilder();
         }
    }

    return _instance;
}
于 2012-04-27T17:49:54.413 回答
5

通过“线程安全”,您是否担心调用您的静态方法的多个线程将获得相同的 PrintStringDataBuilder?答案是否定的,调用是线程安全的。

话虽如此,没有人可以从您给出的小片段中判断该类的其余部分是,还是它的构造函数。类实例不是线程安全的原因有很多。如果他们引用没有锁定的静态属性就是一个例子。

于 2012-04-27T17:51:11.527 回答
3

输入方法始终是线程安全的。访问共享数据可能不是。所以这段代码是线程安全的,因为没有共享数据。

如果您的意图是为所有线程创建一个实例,PrintStringDataBuilder那么您的代码将无法工作。你需要合适的单身人士。在 .NET 4 中,代码可以非常紧凑:

private static Lazy<PrintStringDataBuilder> instance = new Lazy<PrintStringDataBuilder>();

public static PrintStringDataBuilder Instance
{
    get { return instance.Value; }
}

这将保证在每个线程中都PrintStringDataBuilder.Instance将指向相同且只有一个PrintStringDataBuilder对象实例,该实例将以惰性方式创建,即仅在第一次使用时才被创建,并且不会很快。

于 2012-04-27T18:44:15.653 回答
1

@Tejs,

实际上,在 .NET 中,您不需要使用双重检查锁定机制 - 有更好的解决方法。但是如果你选择这样做,你的双重检查锁的实现是不正确的并且不是真正的线程安全的。编译器可以优化初始化_instance = new PrintStringDataBuilder();- 有 3 种可能的修改可以使您的示例真正线程安全:

  1. 内联初始化静态成员 - 绝对是最简单的!
    private static PrintStringDataBuilder _instance = new PrintStringDataBuilder;
    public static PrintStringDataBuilder GetInstance()
    {
        return _instance;
    }

2. 使用 'volatile' 关键字来确保 JIT 的初始化PrintStringDataBuilder不会被 JIT 优化。


private static volatile PrintStringDataBuilder _instance = null;
private static object _lockObject = new object();

public static PrintStringDataBuilder GetInstance()
{
    if(_instance == null)
    {
         lock(_lockObject)
         {
              if(_instance == null)
              {
                 _instance = new PrintStringDataBuilder();
              }
         }
    }

    return _instance;
}

3. 使用带有双重检查锁的 Interlocked.Exchange:


private static PrintStringDataBuilder _instance = null;
private static object _lockObject = new object();

public static PrintStringDataBuilder GetInstance()
{
    if(_instance == null)
    {
         lock(_lockObject)
         {
              if(_instance == null)
              {
                 var temp = new PrintStringDataBuilder();
                 Interlocked.Exchange(ref _instance, temp);
              }
         }
    }

    return _instance;
}

希望这可以帮助。

于 2013-01-16T00:11:42.843 回答