单例模式就是某个类只能创建出一个实例对象,这就是单例模式。
1.单例模式的特点
- 单例类只能创建出一个实例对象。
- 该实例对象必须由单例类内部自行来创建。
- 单例类内部必须提供获取实例对象的接口。
2.单例模式的实现方式
2.1饿汉式单例
字如其意,说明急切的需要此类的实例对象,实现方式就是定义私有的静态成员变量,在类加载时就初始化对象,后面多个线程都获取到的是这个唯一的对象,也就不会存在线程安全的问题。
public class Window {
//饿汉式单例, 静态的成员在类被加载时,就会初始化window对象,只有一份
//没有任何的线程安全问题
private static Window window = new Window();
private Window(){
}
public static Window getWindow(){
return window;
}
}
2.懒汉式单例
懒汉式单例指的是在调用方法时才会创建对象,所以这种会存在线程安全的问题。
初始懒汉式单例:
public class Window1 {
private static Window1 window=null;
private Window1(){
}
/*
在多线程访问时,可能会有多个线程同时进行到if,就会创建出多个对象
*/
public static Window1 getWindow(){
if(window==null){
window = new Window1();
}
return window;
}
}
在高并发的情况下,可能同一时间有多个线程同时进入到if判断,就会创建出多个对象。
Synchronized改进:
public class Window2 {
private static Window2 window=null;
private Window2(){
}
/*
给方法加锁, 可以解决,但是效率低,一次只能有一个线程进入获取
*/
public static synchronized Window2 getWindow(){
if(window==null){
window = new Window2();
}
return window;
}
}
最简单粗暴的方式就是在方法上用Synchronized关键字修饰,但这样效率太低了,同一时间只能有一个线程访问此方法。
Synchronized再改进:
public class Window3 {
private static Window3 window=null;
private Window3(){
}
/*
给代码块加锁,双重检索+synchronized
*/
public static Window3 getWindow(){
if(window==null){
synchronized(Window3.class){
if(window == null){
window = new Window3();
}
}
}
return window;
}
}
改进措施就是双重检索,同一时间可以让多个线程进入第一个if判断,这样保证了效率,然后加锁,代码块内部进行第二个if判断,除了第一个进入的线程,之后已经进入第一个if判断的线程获取到锁时,再次判断发现已经不为null了,就直接返回即可。
volatile+Synchronized:
在并发编程我们了解到,程序可能会出现指令重排的问题,所以为了保险起见我们可以给引用变量加上volatile。
public class Window {
private static volatile Window window=null;
private Window(){
}
public static Window getWindow(){
if(window==null){
synchronized(Window.class){
if(window == null){
window = new Window();
}
}
}
return window;
}
}
3.单例模式的应用
Java中的Runtime类就是单例类,利用此类可以用来获取内存空间,释放空间等等。