Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/webpack/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
多线程环境中的Java单例类行为_Java_Multithreading - Fatal编程技术网

多线程环境中的Java单例类行为

多线程环境中的Java单例类行为,java,multithreading,Java,Multithreading,我读过很多关于这个的问题,但措辞不同,没有回答我想要的。我不希望有任何代码解决方案,只希望对多线程环境中的单例对象进行行为解释。答案与一些解释链接将是伟大的 问题1: 如果有多个线程需要访问singleton对象(比如在spring配置中配置为singleton bean),那么只有一个线程能够访问,其他线程被阻止,直到当前持有线程释放它 对我来说,这里的阻塞是有意义的,因为只有一个对象,所有线程不能同时获得同一个对象 所以,如果我将我的DAO配置为单例bean,那么如果多个用户(线程)尝试通过

我读过很多关于这个的问题,但措辞不同,没有回答我想要的。我不希望有任何代码解决方案,只希望对多线程环境中的单例对象进行行为解释。答案与一些解释链接将是伟大的

问题1: 如果有多个线程需要访问singleton对象(比如在spring配置中配置为singleton bean),那么只有一个线程能够访问,其他线程被阻止,直到当前持有线程释放它

对我来说,这里的阻塞是有意义的,因为只有一个对象,所有线程不能同时获得同一个对象

所以,如果我将我的DAO配置为单例bean,那么如果多个用户(线程)尝试通过该DAO进行读写,那么它实际上不会同时发生,而是依次发生在数据库密集型应用程序中

另一方面,大多数DAO都是无状态的(至少在我的例子中是这样),因此我不需要将其配置为单例,而是可以在任何需要的地方实例化它,并并发地执行操作,但是每个对象实例化可能需要一些时间和内存。因此,这是一个设计决策

在我的例子中,由于DAO没有状态变量,所以内存可以忽略不计,所以如果我的上述理论是正确的,那么我将选择第二个选项,即选择并发而不是内存

问题2:对于所提出的问题

我认为最好的答案是下面的S.Lott(我不知道如何指向链接) 直接发送至答案,以便复制答案):

单身解决一个(而且只有一个)问题

资源争用

如果你有一些资源

(1) 只能有一个实例,并且

(2) 您需要管理单个实例

你需要一个单身汉

这里我的问题是,如果上面的答案是真的-->这将是将记录器实现为单例的原因,因为您只有一个日志文件(单个资源)

如果一个线程将某个日志的一部分写入文件,然后挂起,然后线程2写入其部分,然后线程1再次写入,从而使日志文件变得乱七八糟,那么由于时间分割,您不希望出现这种情况。

我在文本上方划了一行,因为我认为如果一个线程正在记录,那么第二个线程就根本无法记录(阻塞了),因为单例配置,避免了让日志文件变得乱七八糟


我的理解正确吗?

如果您想将多线程和单个日志文件结合起来,而这不是单线程实现的,那么单线程只对一个线程有影响,以避免出现另一个线程

现在,如果您使用多个线程来打破这一点,那么单线程将无法帮助您。如果他们这样做了,第二个线程将无法进行日志记录。

“Singleton”在Java编程语言中没有任何意义。这只是一种设计模式。由多个线程共享的单例对象的行为与由多个线程共享的任何其他对象的行为没有区别


只有当并且只有当你使它安全时,它才是安全的。

无论@james说什么都是正确的。关于你的问题:

问题1:多个线程可以无阻塞地访问singleton对象,除非使返回对象的get方法同步。请参阅下面的内容(多个线程可以获得对singleton对象的引用,而不会被阻止)


问题2:不,你的理解是错误的。如果一个线程正在写入,另一个线程必须等待,这不是因为singleton配置,而是因为对象(文件)可能最终处于不一致的状态;这同样适用于任何未正确同步的对象

您正在链接singleton模式和多线程应用程序,假设必须序列化对singleton对象的访问;事实并非如此

要求单例必须是线程安全的。

考虑你问题中的DAO例子;假设每个调用都是无状态的(即,您不在类中共享变量,而只在方法中共享),您不需要同一DAO的多个实例,在Spring应用程序中通常有一个或多个管理器类(通常使用AOP管理这些管理器类的DB事务);它们中的每一个都有一个对单个DAO的引用。每次调用DAO对象时,它都会从数据源获取一个DB连接,并执行所需的操作,然后释放到数据源的连接

当多个线程调用manager类时,您需要以线程安全的方式获取/释放DB连接。通常,Spring隐藏了这种复杂性,您不必担心这一点

dao的伪代码类似于

public void doSomeDBOperation(YourObject param) {
    Connection connection=acquireDBConnection();//the connection must be acquired in a thread safe way
    SQLStatement statement=connection.createStatement(yourSQL);
    //do the operation with your param;
    releaseDBConnection(connection);
}
Spring也做了一些类似的事情,它在AOP方面获得了一个Db连接,它保存在线程局部变量中,因此它可以被多个DAO使用,并且您可以通过连接管理事务

因此,一个管理器、一个dao和多个DB连接是一种并行管理多线程操作的方法(假设您使用的是连接池,则只序列化DBconnection租约/释放),并且在不阻塞的情况下执行DB操作(在DB级别的java级别上,操作可能会锁定一些东西以保证完整性约束)

关于记录器问题,我假设您指的是大多数日志库的使用方式,即在每个类中声明一个静态记录器:

public class MyClass {
    private static final Logger logger=LoggerFactory.get(MyClass.class.getName());
    .....
}
logger类和您记录的文件没有直接链接,使用Log4J、LogBack等,您可以通过一个日志调用记录到多个文件,甚至可以记录到非文件的文件(例如套接字)。真正的写入操作由appender完成,appender必须是线程安全的,并序列化对参考底图的访问
public class MyClass {
    private static final Logger logger=LoggerFactory.get(MyClass.class.getName());
    .....
}
class Stateful {
    private String s; // shared resource
    void setS(String s) {
        this.s = s;
    }
}

class Stateless {
    int print(String s) {
        return s.size();
    }
}
class Logger {
    private List<String> msgs = new CopyOnWriteArrayList<>();
    void log(String msg) {
        msgs.add(msg);
    }
    private void process() {...} // used internally by a thread specially for Logger
}