单例模式——单线程适用版
概念:
java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
package org.zangyu.Singleton;
public class Singleton {
public static void main(String[] args) {
// TODO Auto-generated method stub
//singleton s0= new singleton();不能调用
singleton1 s1 = singleton1.Getinstance();
singleton1 s2 = singleton1.Getinstance();
if(s1==s2) {
System.out.println("同一个实例");
}else {
System.out.println("不同实例");
}
}
}
class singleton1{
private static singleton1 instance;
private singleton1()
{
//构造方法为private,外界就不能利用new创建此类型的实例
}
public static singleton1 Getinstance()
{//此方法是获得本类实例的唯一全局访问点
if(instance==null) {
instance=new singleton1();
}
return instance;
}
}
该程序只适用于单线程,如果是多线程的话在使用singleton1.Getinstance()方法创建对象时,需要时间而此时nstance==null的结果还未来得及返回,可能会同时创建多个实例。
解决方法:
给进程加锁在执行new创建实例的地方加锁,同时在锁定之前判断是否为null,如果为null就不用进锁。
加锁:
Java中使用synchronized,被synchronized修饰的区域每次只有一个线程可以访问,其他线程要想进锁就必须等待。
单例模式——多线程单锁版
package org.zangyu.Singleton;
public class Singleton2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//singleton s0= new singleton();不能调用
singleton1 s1 = singleton1.Getinstance();
singleton1 s2 = singleton1.Getinstance();
if(s1==s2) {
System.out.println("同一个实例");
}else {
System.out.println("不同实例");
}
}
}
class singleton1{
private static singleton1 instance;
private static final Object syncRoot =new Object();
//程序运行时创建的一个静态只读的进程辅助对象
private singleton1()
{
//构造方法为private,外界就不能利用new创建此类型的实例
}
public static singleton1 Getinstance()
{//此方法是获得本类实例的唯一全局访问点
synchronized(syncRoot) {
//在同一时刻加了锁的部分程序只有一个线程可以进入
if(instance==null) {
instance=new singleton1();
}
System.out.println("创建");
return instance;
}
}
}
缺点:每次调用Getinstance()都会加锁同步,降低了性能。
解决:双重锁定——不同让线程每一次都加锁,而只是在实例未被创建的时候加锁,同时也能保证多线程的安全。
单例模式——多线程双锁版(懒汉式)
package org.zangyu.Singleton;
public class Singleton3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// singleton s0= new singleton();不能调用
singletondcl s1 = singletondcl.Getinstance();
singletondcl s2 = singletondcl.Getinstance();
if (s1 == s2) {
System.out.println("同一个实例");
} else {
System.out.println("不同实例");
}
}
}
class singletondcl {
private static singletondcl instance;
// 程序运行时创建的一个静态只读的进程辅助对象
private singletondcl() {
// 构造方法为private,外界就不能利用new创建此类型的实例
}
public static singletondcl Getinstance() {// 此方法是获得本类实例的唯一全局访问点
if (instance == null) {
//给类加锁,类的所有对象用同一把锁,多个线程可同时到达这里
synchronized (singletondcl.class) {//一个线程进去
if (instance == null) {
instance = new singletondcl();
}
}
}
System.out.println("创建");
return instance;
}
}
单例模式——多线程双锁版(饿汉式)
package org.zangyu.Singleton;
public class Singleton4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// singleton s0= new singleton();不能调用
singletondcl s1 = singletondcl.Getinstance();
singletondcl s2 = singletondcl.Getinstance();
if (s1 == s2) {
System.out.println("同一个实例");
} else {
System.out.println("不同实例");
}
}
}
class singletondcl {
private static singletondcl instance=new singletondcl();
//在自己被加载时就实例化
private singletondcl() {
// 构造方法为private,外界就不能利用new创建此类型的实例
}
public static singletondcl Getinstance() {// 此方法是获得本类实例的唯一全局访问点
//只需要返回即可;保证了线程安全
System.out.println("创建");
return instance;
}
}
饿汉式和懒汉式区别
从名字上来说,饿汉和懒汉
饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
1、线程安全:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,占用资源多。
懒汉式本身是非线程安全的可通过双锁模式解决问题
什么是线程安全?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。
以上部分摘取自朱红梅老师2020年5月的课件。