Java 实例解析单例模式
作者:FranzLiszt1847
单例模式的介绍
单例对象(Singleton)是一种常用的设计模式。在实际使用中,单例对象能保证在一个JVM中,该对象只存在一个实例存在。
优点
1、减少系统开销,提高系统性能
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力
3、避免对共享资源的多重占用
缺点
1、不适应用多变的对象
2、扩展困难
3、单例类的职责过重,在一定程度上违背了“单一职责原则”。
Synchronized
Synchronized示例
介绍单例模式前,我们现介绍一下Synchronized
示例如下:
建立一个内部类,并开启子线程,如果实例该类,则自动执行test1()方法
class SynchronizedTest implements Runnable { private int count; public SynchronizedTest() { count = 0; } @Override public void run() { test1(); } private void test1() { synchronized (this) { for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + count++); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
构造一个SynchronizedTest 对象,传入两个线程对象
SynchronizedTest test = new SynchronizedTest(); Thread thread1 = new Thread(test,"test1"); Thread thread2 = new Thread(test,"test2"); thread1.start(); thread2.start();
由结果可知,当一个对象持有该代码块时,另一个线程访问不到被锁住的代码块,只要当前一线程执行完成,另一线程才能执行。
test1:0
test1:1
test1:2
test1:3
test1:4
test2:5
test2:6
test2:7
test2:8
test2:9
Synchronized与非Synchronized
建立一个内部类
class SynchronizedTest implements Runnable { private int count; public SynchronizedTest() { count = 0; } @Override public void run() { if (Thread.currentThread().getName().equals("S")) { test1(); } else { test2(); } } public void test1() { synchronized (this) { for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } public void test2() { for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + count); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
SynchronizedTest test = new SynchronizedTest(); Thread thread1 = new Thread(test,"S"); Thread thread2 = new Thread(test,"N"); thread1.start(); thread2.start();
由结果可知,一个线程访问Synchronized修饰的代码块,另一个线程访问非Synchronized代码块不受阻塞
S:0
N:1
N:2
S:1
N:2
S:2
S:3
N:4
S:4
N:5
Singleton
第一个示例
此示例实现了单例,但是如果放在多线程当中,将会漏洞百出
我们接着看下一个改良示例
public class Singleton { /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */ private static Singleton instance = null; /* 私有构造方法,防止被实例化 */ private Singleton() { } /* 静态工程方法,创建实例 */ public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
第二个示例
根据第一个示例,我们进行改良,加上Synchronized。
但是每次调用getInstance()方法时,都会对对象上锁,为了减少系统开销,我们一般在第一次创建对象的时候加锁,后面就不需要了
我们接着看下一个改良示例
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
第三个示例
我们对上一个示例进行改良,只有在instance == null的时候,也就是第一次创建对象的时候,执行加锁的区域。此种写法解决了上一个示例遗留的问题,但是在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。
public static Singleton getInstance() { if (instance == null) { synchronized (instance) { if (instance == null) { instance = new Singleton(); } } } return instance; }
第四个示例
此代码初看已经没有问题,如果在构造方法中出现异常,那么实例将得不到创建
public class Singleton { /* 私有构造方法,防止被实例化 */ private Singleton() { } /* 此处使用一个内部类来维护单例 */ private static class SingletonFactory { private static Singleton instance = new Singleton(); } /* 获取实例 */ public static Singleton getInstance() { return SingletonFactory.instance; } }
第五个示例
private static Singleton instance = null; private Singleton(){ } public static Singleton getInstance(){ if (instance == null){ sync(); } return instance; } private static synchronized void sync(){ if (instance == null){ instance = new Singleton(); System.out.println("success"); } }
到此这篇关于Java 实例解析单例模式的文章就介绍到这了,更多相关Java 单例模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!