java 中 单实例和多实例是指的什么?

Python011

java 中 单实例和多实例是指的什么?,第1张

简单的说单例就是

只能有一个实例,所以地方用到的实例都是同一个。

就好像家人吃饭,桌子是单例的,大家用的是同一个。而碗是多例的,每人都有自几的。

下面是具体的例子:

Java模式之单例模式:

单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例。

特点:

1,一个类只能有一个实例

2,自己创建这个实例

3,整个系统都要使用这个实例

例:

在下面的对象图中,有一个"单例对象",而"客户甲"、"客户乙"

和"客户丙"是单例对象的三个客户对象。可以看到,所有的客户对象共享一个单例对象。而且从单例对象到自身的连接线可以看出,单例对象持有对自己的引用。

Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。在很多操作中,比如建立目录

数据库连接都需要这样的单线程操作。一些资源治理器经常设计成单例模式。

外部资源:譬如每台计算机可以有若干个打印机,但只能有一个Printer

Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干个通信端口,系统应当集中治理这些通信端口,以避免一个通信端口被两个请求同时调用。内部资源,譬如,大多数的软件都有一个(甚至多个)属性文件存放系统配置。这样的系统应当由一个对象来治理这些属性文件。一个例子:Windows

回收站。

在整个视窗系统中,回收站只能有一个实例,整个系统都使用这个惟一的实例,而且回收站自行提供自己的实例。因此,回收站是单例模式的应用。

两种形式:

1,饿汉式单例类

public

class

Singleton

{

private

Singleton(){}

//在自己内部定义自己一个实例,是不是很希奇?

//注重这是private

只供内部调用

private

static

Singleton

instance

=

new

Singleton()

//这里提供了一个供外部访问本class的静态方法,可以直接访问

public

static

Singleton

getInstance()

{

return

instance

}

}

2,懒汉式单例类

public

class

Singleton

{

private

static

Singleton

instance

=

null

public

static

synchronized

Singleton

getInstance()

{

//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次

//使用时生成实例,提高了效率!

if

(instance==null)

instance=new

Singleton()

return

instance

}

}

第二中形式是lazy

initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。

注重到lazy

initialization形式中的synchronized,这个synchronized很重要,假如没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。

一般来说第一种比较安全。进入讨论组讨论。

我从我的博客里把我的文章粘贴过来吧,对于单例模式模式应该有比较清楚的解释:

单例模式在我们日常的项目中十分常见,当我们在项目中需要一个这样的一个对象,这个对象在内存中只能有一个实例,这时我们就需要用到单例。

一般说来,单例模式通常有以下几种:

1.饥汉式单例

public class Singleton {

private Singleton(){}

private static Singleton instance = new Singleton()

public static Singleton getInstance(){

return instance

}

}

这是最简单的单例,这种单例最常见,也很可靠!它有个唯一的缺点就是无法完成延迟加载——即当系统还没有用到此单例时,单例就会被加载到内存中。

在这里我们可以做个这样的测试:

将上述代码修改为:

public class Singleton {

private Singleton(){

System.out.println("createSingleton")

}

private static Singleton instance = new Singleton()

public static Singleton getInstance(){

return instance

}

public static void testSingleton(){

System.out.println("CreateString")

}

}

而我们在另外一个测试类中对它进行测试(本例所有测试都通过Junit进行测试)

public class TestSingleton {

@Test

public void test(){

Singleton.testSingleton()

}

}

输出结果:

createSingleton

CreateString

我们可以注意到,在这个单例中,即使我们没有使用单例类,它还是被创建出来了,这当然是我们所不愿意看到的,所以也就有了以下一种单例。

2.懒汉式单例

public class Singleton1 {

private Singleton1(){

System.out.println("createSingleton")

}

private static Singleton1 instance = null

public static synchronized Singleton1 getInstance(){

return instance==null?new Singleton1():instance

}

public static void testSingleton(){

System.out.println("CreateString")

}

}

上面的单例获取实例时,是需要加上同步的,如果不加上同步,在多线程的环境中,当线程1完成新建单例操作,而在完成赋值操作之前,线程2就可能判

断instance为空,此时,线程2也将启动新建单例的操作,那么多个就出现了多个实例被新建,也就违反了我们使用单例模式的初衷了。

我们在这里也通过一个测试类,对它进行测试,最后面输出是

CreateString

可以看出,在未使用到单例类时,单例类并不会加载到内存中,只有我们需要使用到他的时候,才会进行实例化。

这种单例解决了单例的延迟加载,但是由于引入了同步的关键字,因此在多线程的环境下,所需的消耗的时间要远远大于第一种单例。我们可以通过一段测试代码来说明这个问题。

public class TestSingleton {

@Test

public void test(){

long beginTime1 = System.currentTimeMillis()

for(int i=0i<100000i++){

Singleton.getInstance()

}

System.out.println("单例1花费时间:"+(System.currentTimeMillis()-beginTime1))

long beginTime2 = System.currentTimeMillis()

for(int i=0i<100000i++){

Singleton1.getInstance()

}

System.out.println("单例2花费时间:"+(System.currentTimeMillis()-beginTime2))

}

}

最后输出的是:

单例1花费时间:0

单例2花费时间:10

可以看到,使用第一种单例耗时0ms,第二种单例耗时10ms,性能上存在明显的差异。为了使用延迟加载的功能,而导致单例的性能上存在明显差异,

是不是会得不偿失呢?是否可以找到一种更好的解决的办法呢?既可以解决延迟加载,又不至于性能损耗过多,所以,也就有了第三种单例:

3.内部类托管单例

public class Singleton2 {

private Singleton2(){}

private static class SingletonHolder{

private static Singleton2 instance=new Singleton2()

}

private static Singleton2 getInstance(){

return SingletonHolder.instance

}

}

在这个单例中,我们通过静态内部类来托管单例,当这个单例被加载时,不会初始化单例类,只有当getInstance方法被调用的时候,才会去加载

SingletonHolder,从而才会去初始化instance。并且,单例的加载是在内部类的加载的时候完成的,所以天生对线程友好,而且也不需要

synchnoized关键字,可以说是兼具了以上的两个优点。

4.总结

一般来说,上述的单例已经基本可以保证在一个系统中只会存在一个实例了,但是,仍然可能会有其他的情况,导致系统生成多个单例,请看以下情况:

public class Singleton3 implements Serializable{

private Singleton3(){}

private static class SingletonHolder{

private static Singleton3 instance = new Singleton3()

}

public static Singleton3 getInstance(){

return SingletonHolder.instance

}

}

通过一段代码来测试:

@Test

public void test() throws Exception{

Singleton3 s1 = null

Singleton3 s2 = Singleton3.getInstance()

//1.将实例串行话到文件

FileOutputStream fos = new FileOutputStream("singleton.txt")

ObjectOutputStream oos =new ObjectOutputStream(fos)

oos.writeObject(s2)

oos.flush()

oos.close()

//2.从文件中读取出单例

FileInputStream fis = new FileInputStream("singleton.txt")

ObjectInputStream ois = new ObjectInputStream(fis)

s1 = (Singleton3) ois.readObject()

if(s1==s2){

System.out.println("同一个实例")

}else{

System.out.println("不是同一个实例")

}

}

输出:

不是同一个实例

可以看到当我们把单例反序列化后,生成了多个不同的单例类,此时,我们必须在原来的代码中加入readResolve()函数,来阻止它生成新的单例

public class Singleton3 implements Serializable{

private Singleton3(){}

private static class SingletonHolder{

private static Singleton3 instance = new Singleton3()

}

public static Singleton3 getInstance(){

return SingletonHolder.instance

}

//阻止生成新的实例

public Object readResolve(){

return SingletonHolder.instance

}

}

再次测试时,就可以发现他们生成的是同一个实例了。

JAVA单例模式的几种实现方法

1.饿汉式单例类

package pattern.singleton

//饿汉式单例类.在类初始化时,已经自行实例化

public class Singleton1 {

//私有的默认构造子

private Singleton1() {}

//已经自行实例化

private static final Singleton1 single = new Singleton1()

//静态工厂方法

public static Singleton1 getInstance() {

return single

}

}

2.懒汉式单例类

package pattern.singleton

//懒汉式单例类.在第一次调用的时候实例化

public class Singleton2 {

//私有的默认构造子

private Singleton2() {}

//注意,这里没有final

private static Singleton2 single

//只实例化一次

static{

single = new Singleton2()

}

//静态工厂方法

public synchronized static Singleton2 getInstance() {

if (single == null) {

single = new Singleton2()

}

return single

}

}

在上面给出懒汉式单例类实现里对静态工厂方法使用了同步化,以处理多线程环境。有些设计师在这里建议使用所谓的"双重检查成例".必须指出的是,"双重检查成例"不可以在Java 语言中使用。不十分熟悉的读者,可以看看后面给出的小节。 同样,由于构造子是私有的,因此,此类不能被继承。饿汉式单例类在自己被加载时就将自己实例化。即便加载器是静态的,在饿汉式单例类被加载时仍会将自己实例化。单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。从速度和反应时间角度来讲,则比懒汉式单例类稍好些。然而,懒汉式单例类在实例化时,必须处

理好在多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费时间。这意味着出现多线程同时首次引用此类的机率变得较大。

饿汉式单例类可以在Java 语言内实现, 但不易在C++ 内实现,因为静态初始化在C++ 里没有固定的顺序,因而静态的m_instance 变量的初始化与类的加载顺序没有保证,可能会出问题。这就是为什么GoF 在提出单例类的概念时,举的例子是懒汉式的。他们的书影响之大,以致Java 语言中单例类的例子也大多是懒汉式的。实际上,本书认为饿汉式单例类更符合Java 语言本身的特点。

3.登记式单例类.

package pattern.singleton

import java.util.HashMap

import java.util.Map

//登记式单例类.

//类似Spring里面的方法,将类名注册,下次从里面直接获取。

public class Singleton3 {

private static Map<String,Singleton3>map = new HashMap<String,Singleton3>()

static{

Singleton3 single = new Singleton3()

map.put(single.getClass().getName(), single)

}

//保护的默认构造子

protected Singleton3(){}

//静态工厂方法,返还此类惟一的实例

public static Singleton3 getInstance(String name) {

if(name == null) {

name = Singleton3.class.getName()

System.out.println("name == null"+"--->name="+name)

}

if(map.get(name) == null) {

try {

map.put(name, (Singleton3) Class.forName(name).newInstance())

} catch (InstantiationException e) {

e.printStackTrace()

} catch (IllegalAccessException e) {

e.printStackTrace()

} catch (ClassNotFoundException e) {

e.printStackTrace()

}

}

return map.get(name)

}

//一个示意性的商业方法

public String about() {

return "Hello, I am RegSingleton."

}

public static void main(String[] args) {

Singleton3 single3 = Singleton3.getInstance(null)

System.out.println(single3.about())

}

}