1.单例模式:
饿汉式 (可用) public class Demo{
private static Demo demo = new Demo();
private Demo(){
}
public static Demo getInstance(){
return demo;
}
}
懒汉式双锁(不可用) public class Demo{
private static Demo demo;
private Demo(){
}
public static Demo getInstance(){
if(demonull){
synchronized(this)
if(demonull){
demo = new Demo();
}
}
return demo;
}
}
bug:
缺陷:instance=new Singleton();
它不是一个原子操作,这句话做了3件事:
1.给Singleton的实例分配内存
2.初始化Sinhleton的构造器
3.将instance对象指向分配的内存空间(这时instance就非null了)
由于Java编译器允许处理器乱序执行,上面的顺序可能是1-2-3,也可能是1-3-2,这样就会出现问题。
原子操作:这种操作一旦开始,就一直运行到结束,中间不会切换到另一个线程
补充:在Java中设置变量值的操作,除了long和double类型的变量外都是原子操作
在J2SE 5.0中,这一问题被修正了。volatile关键字保证多个线程可以正确处理单件实例
volatile确保本条指令不会被编译器优化,且要求每次直接读值
1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
2)它会强制将对缓存的修改操作立即写入主存,对volatile变量所有的写操作都能立刻被其他线程得知,保证此变量对所有线程的可见性,但是这并不代表基于volatile变量的运算在并发下是安全的,因为volatile只能保证内存可见性,却没有保证对变量操作的原子性。
3)如果是写操作,它会导致其他CPU中对应的缓存行无效。
volatile和synchronized:
volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
volatile仅能实现变量的修改可见性和有序性,但不具备原子特性,而synchronized则可以保证变量的修改可见性和原子性,有序性
volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
volatile标记的变量不会被编译器优化,一定程度上保证有序性,而synchronized标记的变量可以被编译器优化.
volatile比普通变量开销大,它需要插入内存屏障指令来保证处理器不会发生乱序执行,比synchronized开销小
推荐用:
public class Demo{
private static volatile Demo demo;
private Demo(){
}
public static Demo getInstance(){
if(demonull){
synchronized(this)
if(demonull){
demo = new Demo();
}
}
return demo;
}
}
Universal-Image-Loader和Eventbus就是通过此种方式来创建单例
静态内部类(推荐用)
public class Demo{
private Demo(){
}
private static class DemoInstance(){
private static final Demo demo = new Demo();
}
public static Demo getInstance(){
return DemoInstance.demo;
}
}
枚举(推荐用)
enum里面的枚举数据都是线程安全的,而enum实现的单例又是最简单的,Stackoverflow上最赞同的,也是Effective Java的作者推荐的方式
c++实现
懒汉:
class single{
private:
single() {}
static single* s;
public:
get_instance();
};
single* single::s == NULL;
single* single::get_instance(){
if (s == NULL) {
s = new single();
}
return s;
}
饿汉:(需要双锁和volatile)
class single{
private:
single() {}
static single* s;
public:
get_instance();
};
single* single::s = new single();
single* single::get_instance(){
return s;
}
局部静态对象:
class Singleton{
private:
Singleton(){}
Singleton(const Singleton&);
Singleton& operator=(const Singleton& rhs);
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
};
工厂模式
将具体创建产品实例的过程封装在工厂类中,实现客户端和创建产品实例的解耦,让客户端只负责消费,满足java设计原则的单一职责原则和面向对象的封装性(不用到处new实例)
简单工厂 :用来生产同一等级结构中的产品(不方便增加新产品,不修改代码无法扩展)。
工厂方法 :用来生产同一等级结构中的产品(支持增加新产品)。
抽象工厂:用来生产不同产品族的全部产品。(支持增加产品族)。
代理模式
概念:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式其实就是在访问对象时引入一定程度的间接性,因为这种间接性可以附加多种用途。
优点(我们为什么要用代理模式):
代理类可以在执行真实对象操作时,附加其他的操作,比如权限控制,相当于对真实对象进行封装,同时也能协调好调用者与被调用者,降低系统耦合性。
1.虚拟代理,根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。(大图片的预览图)
2.安全代理,用来控制真实对象访问时的权限。(会员和游客)
3.智能指引,是指当调用真实对象时,代理处理另外一些事。(之前做什么,之后做什么)
动态代理:反射。AOP(代理对象不明确)
Proxy类static Object newProxyInstance(ClassLoader loader,Class[ ] interfaces,InvocationHandler h):
loader:定义由哪个classloader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象的invoke方法上
public Object invoke(Object proxy,Method method,Object[ ]args) throws Throwable
{
System.out.println(“before calling”);
Object result=method.invoke(sub,args);
System.out.println(“after calling”);
return result;
}
AOP和OOP区别:
AOP面向切面,横向,从左到右;OOP面向对象,纵向,从上到下