保证一个类只有一个实例,并且提供一个全局访问点
线程池、数据库连接池
延迟加载、使用的时候才进行实例化
①线程安全问题
②double check 加锁,双重检查
③编译器(JIT),CPU有可能对指令进行重排序,导致使用到尚未初始化的实例但已经进行引用赋值,可以通过添加volatile 关键字进行修饰,对于volatile修饰的字段,可以防止指令重排。
单线程:没有问题
package com.jason.designmode.singleton;
public class LazySingletonTest {
public static void main(String[] args) {
LazySingleton instance = LazySingleton.getInstance();
LazySingleton instance1 = LazySingleton.getInstance();
System.out.println(instance==instance1);
}
}
class LazySingleton{
//私有成员变量
private static LazySingleton instance;
//私有构造方法,防止在外部进行实例化
private LazySingleton(){};
public static LazySingleton getInstance(){
if (instance==null){
instance = new LazySingleton();
}
return instance;
}
}
返回ture
多线程:出现两个不同的对象
package com.jason.designmode.singleton;
public class LazySingletonTest {
public static void main(String[] args) {
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
}
}
class LazySingleton{
//私有成员变量
private static LazySingleton instance;
//私有构造方法,防止在外部进行实例化
private LazySingleton(){};
public static LazySingleton getInstance(){
if (instance==null){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazySingleton();
}
return instance;
}
}
com.jason.designmode.singleton.LazySingleton@20917dd4
com.jason.designmode.singleton.LazySingleton@8124ccf
解决方法1
方法上加synchronized
问题:我们不需要每次都对这个方法进行加锁,只要确保没有实例化的时候加锁就行
package com.jason.designmode.singleton;
public class LazySingletonTest {
public static void main(String[] args) {
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
}
}
class LazySingleton{
//私有成员变量
private static LazySingleton instance;
//私有构造方法,防止在外部进行实例化
private LazySingleton(){};
public synchronized static LazySingleton getInstance(){
if (instance==null){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazySingleton();
}
return instance;
}
}
解决方法2:
在创建的时候加锁,并再次进行非空判断
问题:在字节码层面,可能会存在引用赋值的时候另一个线程过来了,会更加if(instance==null)进行判断,发现是有值了,直接返回instance,这时候就会造成空指针异常
解决:加volatile防止字节码重排序:1、分配空间2、引用赋值3、初始化
package com.jason.designmode.singleton;
public class LazySingletonTest {
public static void main(String[] args) {
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
}
}
class LazySingleton{
//私有成员变量
private volatile static LazySingleton instance;
//私有构造方法,防止在外部进行实例化
private LazySingleton(){};
public synchronized static LazySingleton getInstance(){
if (instance==null){
synchronized (LazySingleton.class){
if (instance==null){
instance = new LazySingleton();
//字节码层面,初始化过程123,132
//1.分配空间
//3.应用赋值
//2.初始化
}
}
}
return instance;
}
}
类加载的初始化阶段完成了实例的初始化。本质就是借助于jvm类加载机制,保证实例的唯一性
类加载过程:
1.加载二进制数据到内存中,在方法区生成对应的Class数据接口
2,连接: a.验证,b.准备(给类的静态成员变量赋默认值),c.解析
3,初始化:给类的静态变量赋初值
只有在真正使用对应的类时,才会触发初始化如( 当前类是启动类即main函数所在类,直接进行new操作,访问静态属性、访问静态方法,用反射访问类,初始化-个类的子类等)
package com.jason.designmode.singleton;
public class HungrySingletonTest {
public static void main(String[] args) {
HungrySingleTon instance = HungrySingleTon.getInstance();
HungrySingleTon instance1 = HungrySingleTon.getInstance();
System.out.println(instance==instance1);
}
}
class HungrySingleTon{
private static HungrySingleTon instance = new HungrySingleTon();
private HungrySingleTon(){};
public static HungrySingleTon getInstance(){
return instance;
}
}
定义了对象之间一对多的关系,当主题对象发生改变的时候,所依赖他的对象也会收到通知并发生改变。
主题类:容器、添加观察者、移除观察者、通知
接口:定义通知方法
观察者类:实现通知方法接口
package com.jason.designmode;
import java.util.ArrayList;
import java.util.List;
/**
* 观察者模式
*/
public class Observertest {
public static void main(String[] args) {
subject subject = new subject();
Task1 task1 = new Task1();
subject.addObserver(task1);
Task2 task2 = new Task2();
subject.addObserver(task2);
subject.notifyObserver("xxxxx");
System.out.println("--------------------");
subject.removeObserver(task2);
subject.notifyObserver("xxxxx");
}
}
class subject{
// 容器
private List container = new ArrayList();
// add
public void addObserver(Observer observer){
container.add(observer);
}
// remove
public void removeObserver(Observer observer){
container.remove(observer);
}
// 通知
//当数据发生改变的时候通知观察者
public void notifyObserver(Object object){
for (Observer observer : container) {
observer.update(object);
}
}
}
/**
*
* 定义为接口,因为有多个观察者实现
*/
interface Observer{
//当主题对象发生改变的时候,被主题对象调用来通知
void update(Object object);
}
class Task1 implements Observer{
@Override
public void update(Object object) {
System.out.println("received1:"+object);
}
}
class Task2 implements Observer{
@Override
public void update(Object object) {
System.out.println("received2:"+object);
}
}
1.符合开闭原则,对拓展开发对修改关闭(interface Observer)
2.可以在运行时建立对象间的关系(addObserver和removeObserver)
1.JDK:Observer类
2.spring:ApplicationListener类