继续来复习常用的设计模式-单例模式,顺便回忆一下线程安全的几种实现方式。
一、什么是单例模式
单例模式,简单常用的一种设计模式,也很好的体现了代码控制对象在内存数量的一种方式,主要分2种实现方式:
①饿汉式,线程安全
②懒汉式,线程不安全(添加锁机制,可以实现线程安全)
个人理解:顾名思义,就是单例,单个实例,在内存当中保持只有一个实例的方式。
解决问题:控制了类的实例化数量,减少了不必要的内存消耗。
二、demo演示
⑴饿汉式(线程安全):
/**
* @param
* @Title: Dog
* @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
* 饿汉模式,类加载时,就实例化对象,造成内存浪费,线程安全的,使用静态常量变量,保证实例唯一。
* @return
* @throws
*/
public class Hungry_dog {
private static final Hungry_dog dog=new Hungry_dog();
private Hungry_dog(){
}
public static Hungry_dog getDog(){
return dog;
}
public void call(){
System.out.println("单例模式饿汉式,线程安全");
}
}
1.为了首次加载类时,就实例化对象,则必须将实例放在静态代码区(因为只加载一次,所以并发访问时,不存在线程安全问题)。
2.为了实现类的对象实例在内存当中唯一,不提供使用new方式进行实例化,故把构造函数设为私有化。
3.但得必须提供一个外部可以获取该实例的方法,所以必须实现一个对外开放的获取实例的方法。
⑵懒汉式[延迟/按需加载](线程不安全)
/**
* @param
* @Title: Dog
* @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
* 懒汉模式,类加载时,不实例化对象,等被调用获取对象方法时再实例化对象
* 线程不安全
* @return
* @throws
*/
public class Lazy_dog{
private static Lazy_dog lazy_dog;
private Lazy_dog(){}
public static Lazy_dog getLazy_dog(){
if(lazy_dog==null){
try {
Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
Object object=c.newInstance();
Thread.sleep(3000);//测试线程是否安全
lazy_dog=(Lazy_dog)object;
}catch (Exception e){
System.out.println("反射创建实例Lazy_dog失败");
}
}
return lazy_dog;
}
public void call(){
System.out.println("Lazy_dog-反射创建实例成功!");
}
}
测试一下线程是否安全:
public class Test {
public static void main(String[] args) {
//测试线程是否安全
int a=0;
while (a<10){
new Thread(new Runnable() {
@Override
public void run() {
Lazy_dog lazy_dog= Lazy_dog.getLazy_dog();
System.out.println(lazy_dog);
}
}).start();
a++;
}
}
}
输出:可以看到在多线程的情况下产生了很多实例,证明确实是线程不安全
⑶懒汉式[延迟/按需加载](线程安全),最简单的处理方式,在创建实例的方法上添加同步锁
优点:线程安全
缺点:性能下降,并且把整个类的对象添加的线程锁
/**
* @param
* @Title: Dog
* @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
* 懒汉模式,类加载时,不实例化对象,等被调用获取对象方法时再实例化对象
* 添加方法同步锁-线程安全
* @return
* @throws
*/
public class Lazy_dog{
private static Lazy_dog lazy_dog;
private Lazy_dog(){}
public synchronized static Lazy_dog getLazy_dog(){
if(lazy_dog==null){
try {
Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
Object object=c.newInstance();
Thread.sleep(3000);
lazy_dog=(Lazy_dog)object;
}catch (Exception e){
System.out.println("反射创建实例Lazy_dog失败");
}
}
return lazy_dog;
}
public void call(){
System.out.println("Lazy_dog-反射创建实例成功!");
}
}
输出:可以看到在多线程的情况下产生同一实例
⑷懒汉式[延迟/按需加载](线程安全),在创建实例的方法内特定的代码添加同步锁
优点:线程安全,性能较好
缺点:对性能还是较大影响
/**
* @param
* @Title: Dog
* @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
* 懒汉模式,类加载时,不实例化对象,等被调用获取对象方法时再实例化对象
* 添加同步锁-线程安全
* @return
* @throws
*/
public class Lazy_dog{
private static Lazy_dog lazy_dog;
private Lazy_dog(){}
public static Lazy_dog getLazy_dog(){
String temp="test";
synchronized(temp){
if(lazy_dog==null){
try {
Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
Object object=c.newInstance();
Thread.sleep(3000);
lazy_dog=(Lazy_dog)object;
}catch (Exception e){
System.out.println("反射创建实例Lazy_dog失败");
}
}
}
return lazy_dog;
}
public void call(){
System.out.println("Lazy_dog-反射创建实例成功!");
}
}
输出:可以看到在多线程的情况下产生同一实例
⑸懒汉式[延迟/按需加载](线程安全),在创建实例的方法内特定的代码添加重入锁
优点:线程安全,性能较好,重入锁更灵活
缺点:对性能有影响
/**
* @param
* @Title: Dog
* @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
* 懒汉模式,类加载时,不实例化对象,等被调用获取对象方法时再实例化对象
* 添加同步锁-线程安全
* @return
* @throws
*/
public class Lazy_dog{
private static Lazy_dog lazy_dog;
private static Lock lock=new ReentrantLock();
private Lazy_dog(){}
public static Lazy_dog getLazy_dog(){
lock.lock();
if(lazy_dog==null){
try {
Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
Object object=c.newInstance();
Thread.sleep(3000);
lazy_dog=(Lazy_dog)object;
}catch (Exception e){
System.out.println("反射创建实例Lazy_dog失败");
}
}
lock.unlock();
return lazy_dog;
}
public void call(){
System.out.println("Lazy_dog-反射创建实例成功!");
}
}
输出:可以看到在多线程的情况下产生同一实例
以上2-5都是懒汉式实现的单例模式,各有优缺点,主要演示线程安全的几种方式。
1.第2个例子为了首次加载类时,不实例化对象,在首次调用时,才实例化对象(因为首次调用时,有可能发生多线程同时调用,所以并发访问时,存在线程安全问题)。
2.第3-5个例子为了首次加载类时,不实例化对象,在首次调用时,才实例化对象(因为首次调用时,有可能发生多线程同时调用,因此使用线程锁的方式进行限制,所以并发访问时,也不存在线程安全问题)。
3.为了实现类的对象实例在内存当中唯一,不提供使用new方式进行实例化,故把构造函数设为私有化。
4.但得必须提供一个外部可以获取该实例的方法,所以必须实现一个对外开放的获取实例的方法。
至此,单例模式的demo演示完了,如有错漏,请告知。