单例类不能工作



我试图使Clients类单例,但它不工作。这是我的类:

public class Clients {
    private static Clients instance = null;
    private ArrayList<Client> cList;
    private Clients() {
        cList = new ArrayList<Client>();
    }
    public static Clients getInstance() {
        if (instance == null) {
            System .out.println("created");
            instance = new Clients();
        }
        return instance;
    }
    public static ArrayList<Client> getcList() {
        return getInstance().cList;
    }
    public static void setcList(ArrayList<Client> cList) {
        getInstance().cList = cList;
    }
}

我在两个不同的类中获得这个实例(都有自己的main函数)。在一个类中获得它的实例后,我在另一个类中获得它,但这两个类仍在执行。

无论何时实现单例,getInstance()方法都应该是线程安全的。

public static synchronized Clients getInstance()

…还是…

private static final Object INSTANCE_LOCK = new Object();
public static Clients getInstance() {
    synchronized(INSTANCE_LOCK) {
        if(instance == null) instance = new Clients();
    }
    return instance;
}
当然,如果实际上是在两个不同的程序中执行这段代码,而不是在两个不同的线程中执行,那么就会有两个实例。我假设是前者,因为后者使你的问题变得毫无意义。

我想我应该解释为什么这是荒谬的。

当您使用main(String[] args)方法执行Java程序时,所有的类都被加载到JVM中。如果您随后执行另一个程序,您将获得另一个JVM和所有相关类的另一个"副本"。这样,您就有了两个单独的单例对象——每个程序一个。两者之间不共享类

您提到两个类"都有自己的main",所以我假设您有两个独立的程序。

长话短说,两个程序之间并不真正共享数据。单例类将确保在单个程序中只有一个对象的实例,但是两个程序仍然是完全相互独立的,不能以这种方式共享数据。

即使只有一个带有"main"的类并且只运行两次,也会出现这种情况。

如果你想在程序之间像这样共享数据,你有很多很多的选择,但其中一些是:

  • 看看你是否可以把两个独立的程序合并成一个。你真的需要两个程序吗?
  • 使用数据库来存储数据,MySQL和SQLite是两个简单的选择。
  • 一个程序可以向文件写入数据,另一个程序可以读取数据。
  • 从一个程序到另一个程序发送数据有很多其他的选择,比如套接字(已经存在无数的网络协议,加上你可以自己推出),特定于平台的东西,如Windows上的命名管道,共享内存等。查看"java ipc"(进程间通信——这些是允许两个程序相互通信的通用技术)的Google结果。

您可以像Jeff Gohlke所说的那样使用上面的同步块,但是您可能还想考虑使用锁。

锁最好的地方是synchronized关键字不提供公平性,而我们可以在创建ReentrantLock对象时将公平性设置为true,以便等待时间最长的线程首先获得锁。

// Fairness set to false is faster than a synchronized block.
private static final ReentrantLock rlock = new ReentrantLock(false);
public static final Clients getInstance() {
    rlock.lock();
    try {
        System.out.printf("[Thread %s] Clients.getInstance()%n",
            Thread.currentThread().getName());
        if (instance == null) {
            instance = new Clients();
        }
        return instance;
    } finally {
        rlock.unlock();
    }    
}

你可以通过执行

来解决很多问题

private static Clients instance = new Clients()

无需担心get实例中的锁定习惯用法。除非你正在构建的类的构建成本很高,否则做这种惰性实例化是没有意义的。

说到这里,我也不确定我是否喜欢cList的getter和setter。我更喜欢这些是你正在创建单例的类的实例方法,所以你会做(例如)

Clients clients = Clients.getInstance();
clients.getcList();

并且,如果这是一个多线程环境,那么你必须意识到setter可能会影响其他已经有对单例对象引用的线程。

最新更新