1.设计模式
单例模式
单例模式有哪几种实现方式,什么场景该使用静态方法实现,什么场景该使用双检锁实现
单例模式
线程安全实现的常见三种方法:
1. 静态初始化(饿汉).不管是否使用都会创建
2. 双检锁(懒汉).单例变量必须要用volatile修饰.
3. 单例注册表.spring中bean的单例模式就是用该方法实现.
1、构造方法私有化,让出了自己类中能创建外其他地方都不能创建
2、在自己的类中创建一个单实例(饱汉模式是一出来就创建创建单实例,而饥汉模式需要的时候才创建)
3、提供一个方法获取该实例对象(创建时需要进行方法同步)
python中日志处理
单例模式保证一个类仅有一个实例,并提供一个访问的他的全局访问点。
由于单例模式和日志类的应用场景很相似,因为文件只能被一个进程打开。
所以使用单例模式获取日志文件的实例对象,避免了一个文件多次被打开造成的异常。
import os,logging,sys,time
def singleton(cls):
instances = {}
def _singleton(*args,**kwargs):
if cls not in instances:
instances[cls] = cls(*args,**kwargs)
return instances[cls]
return _singleton
@singleton
class Logger():
def __init__(self,logfile=None):
self.logger = logging.getLogger()
formater = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(filename)s %(lineno)d '
'%(thread)d %(threadName)s %(process)d %(message)s')
if logfile == None:
cur_path = os.path.split(os.path.realpath(__file__))[0]
stime = time.strftime("%Y-%m-%d",time.localtime())
logfile = cur_path + os.sep + "log_" + stime + ".log"
else:
logfile = logfile
self.sh = logging.StreamHandler(sys.stdout)
self.sh.setFormatter(formater)
self.fh = logging.FileHandler(logfile)
self.fh.setFormatter(formater)
self.logger.addHandler(self.sh)
self.logger.addHandler(self.fh)
self.logger.setLevel(logging.WARNING)
if __name__ == "__main__":
lg = Logger()
lg.logger.warning("aaa1")
1、单例模式概念:
顾名思义,单例模式指的是在软件系统运行过程中,某个类只存在一个实例。因此一个类实现单例模式时需要具备以下3个条件:
1)类的内部定义一个该类的静态私有成员变量;
2)构造方法为私有;
3)提供静态工厂方法,供外部获取类的实例;
2、单例模式实现方式:
1)懒汉式单例
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式单例即在使用使用时才创建类的实例,工厂方法增加类synchronized关键字以保证线程安全。在多线程访问的环境下,同步方法导致系统性能下降。优点在于,类加载时不用自行实例化对象,避免加载缓慢。
2)饿汉式单例
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉式单例,在类加载式自行实例化对象。优点在于多线程环境下不会出现线程安全问题,因为类只加载一次。缺点在于,系统加载时消耗额外资源,如果该实例没有使用的情况会造成资源浪费。
3)双重检测单例
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重检测锁定(Double-Check Locking)方案属于懒汉式,使用延时加载技术,避免类加载时任务过重和造成资源浪费,同时将synchronized关键字加在代码块中,减少线程同步锁定以提升系统性能。instance实例使用了volatile关键字修饰,主要是避免在多线程环境下由于编译器进行的重排序操作而导致的线程安全问题。JVM在创建一个对象时会进行以下步骤:
1)分配对象内存空间;
2)初始化对象;
3)设置instance指向分配的内存地址;
编译器为了优化性能,可能会将2、3操作调换顺序,假设A线程在执行new Singleton()方法时,由于2、3操作重排序,而初始化对象操作尚未完成时释放了锁。线程B获取锁之后会发现instance已经不为空,当线程B获取到instance对象后如果直接使用就会出错,原因就是对象没有进行初始化操作。而volatile关键字能避免重排序,因此能保证线程安全。总体上来说,双重检测由于加了锁,多线程并发下还是会有效率问题。
4)枚举单例
public enum Singleton {
INSTANCE
枚举单例模式,不会存在线程安全问题,不过在Android平台下,枚举类性能相对较低。
5)静态内部类单例
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonFactory.instance;
}
static class SingletonFactory {
private final static Singleton instance = new Singleton();
}
}
工厂模式:Spring IOC就是使用了工厂模式.
对象的创建交给一个工厂去创建。
代理模式:Spring AOP就是使用的动态代理。
工厂模式
在实际业务中经常用到,也是面试的主要考察点.是创建不同类型实例常用的方式.
spring中的bean都是由不同工厂类创建的.
代理模式
在不适合或不能直接引用另一个对象的场景,可以用代理模式对被代理的队形进行访问行为的控制.Java的代理模式分为静态代理和动态代理,静态代理是指在编译时就创建好的代理类,例如在源代码中编写的类.动态代理指在JVM运行过程中动态创建的代理类,如JDK动态代理,CDLIB,javaasist等.
例如,在Mybatis中getMapper时会通过MapperProxyFactory及配置文件动态生成的Mapper代理对象,代理对象会拦截Mapper接口的方法调用,创建对应方法的MapperMethod类并执行execute方法,然后返回结果.
责任链模式
类似工厂流水线,其中的每个节点完成对对象的某一种处理.
Netty框架的处理消息的Pipeline就是采用的责任链模式.
适配器模式
类似于转接头,将两种不匹配的对象进行适配,也可以起到对两个不同的对象进行解耦的作用.
SLF4J可使项目与Log4、logback等具体日志实现框架进行解耦,其通过不同适配器与不同框架进行适配,完成日志功能的使用.
观察者模式
也可称为发布订阅模式,适用于一个对象某个行为需要触发一系列操作的场景.
GRPC中stream流式请求的处理.
构造者模式
适用于一个对象拥有很多复杂的属性,需要根据不同情况创建不同的具体对象.
创建Protocol Buffer对象时,需要用到Builder