单例模式
单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”
特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
Java中简单的单例模式(不牵扯并发)注:这个是预加载,如果懒加载的话可以直接声明变量的时候创建对象。
/**
* 单例模式类
* @author wanghao
*
*/
class SinglePattern{
private static SinglePattern instance ;
//1.构造方法私有化
private SinglePattern() {
}
//2.提供一个全局访问点
public static SinglePattern getInstance() {
if(instance == null) {
instance = new SinglePattern();
}
return instance;
}
}
上面的那种方法是线程不安全的,如果并发则会创建多个对象,下面可以用一个demo测试以下,
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(SinglePattern.getInstance());
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(SinglePattern.getInstance());
}
}
});
t1.start();
t2.start();
}
创建两个线程,线程里面分别创建对象。执行结果为:
会出现两个对象。这样就不符合我们的单例模式只有一个实例的思想了。
多线程有一个锁机制。使用synchronized修饰。我们可以锁住获取对象的方法。代码如下:
/**
* 加锁后的,单例模式类
* @author wanghao
*
*/
class SinglePattern{
private static SinglePattern instance ;
//1.构造方法私有化
private SinglePattern() {
}
//2.提供一个全局访问点 加锁后
public static synchronized SinglePattern getInstance() {
if(instance == null) {
instance = new SinglePattern();
}
return instance;
}
}
然后下面我们创建的对象就只有一个实例了。每次获取的都是一个实例。
众所周知线程安全就等于效率会相对低点。所有经过改造优化,出现另一种双重判断方式。代码如下:
/**
* 加锁后的,单例模式类 +优化后
* @author wanghao
*
*/
class SinglePattern{
private static SinglePattern instance ;
//1.构造方法私有化
private SinglePattern() {
}
//2.提供一个全局访问点 加锁后
public static SinglePattern getInstance() {
if(instance == null) {
synchronized(SinglePattern.class) {
if(instance == null) {
instance = new SinglePattern();
}
}
}
return instance;
}
}
单例模式重点就一个对象,这样理论上会是创建次数少获取的次数多。所以,我们只需加锁创建对象的方式,而判断是否为null,可以不加锁。可以提高效率。
如果按前面一种方式,如果高并发10个线程同时访问,则需要耗费10*(等待时间+判断时间),
而如果用第二张方式,如果通用10个线程访问,则只需10*(判断时间),如果没有对象则再加上一次判断时间和创建对象的时间。
有的人会问,上面只是为了优化, 需要两个if判断。我们先用一个判断人后只需上面的代码,验证结果后在说原因吧。为去掉锁里面的判断执行结果为:
两个实例,这是为什么呢?
这是因为刚开始instance实例肯定是null的,T1和T2线程里面都是空实例,所以都过了if判断,然后到达锁门口,T2跑的快拿到锁了,进去创建了个对象出去了。(注:这时候已经有对象了)然后因为T1线程已经通过判断,到达创建对象锁门口,等T1归还了锁之后,T1线程进入创建对象。然后出去。这时候两个线程就创建了两个对象。
所以才要在锁里面判断对象是否为空。
工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
把对象比作产品,创建对象的地方叫做工厂,如:Spring中的Bean和容器。
简单工厂模式 ,代码实现:
一个抽象类水,和三个子类,农夫三拳,哇哈哈,百岁山类。
/**
* 我家卖水的
* @author wanghao
*
*/
abstract class Water{
//卖的是可以喝的水
public abstract void drink();
}
//农夫三拳 水
class NongFuWater extends Water{
@Override
public void drink() {
System.out.println("农夫三拳有点甜。");
}
}
//哇哈哈 水
class WaHaWater extends Water{
@Override
public void drink() {
System.out.println("哇哈哈也有点甜。");
}
}
//百岁山 水
class BaiSuiWater extends Water{
@Override
public void drink() {
System.out.println("百岁山也是甜的。");
}
}
然后创建个工厂存放我的水。进行销售:
class WaterFactory{
public static Water getWater(String waterName) {
Water water = null;
switch (waterName) {
case "农夫三拳":
water = new NongFuWater();
break;
case "哇哈哈":
water = new WaHaWater();
break;
case "百岁山":
water = new BaiSuiWater();
break;
}
return water;
}
}
然后写测试类:
public static void main(String[] args) {
//等待顾客买水
Water water = null;
System.out.println("来个农夫三拳");
//去我工厂拿水
water = WaterFactory.getWater("农夫三拳");
water.drink();
System.out.println("还渴,再来瓶哇哈哈");
//去我工厂拿水
water = WaterFactory.getWater("哇哈哈");
water.drink();
System.out.println("还渴,再来瓶百岁山");
//去我工厂拿水
water = WaterFactory.getWater("百岁山");
water.drink();
System.out.println("还渴,再来瓶洛阳宫");
//去我工厂拿水
water = WaterFactory.getWater("洛阳宫");
if(water == null) {
System.out.println("没有该水。");
}
}
运行结果为:
这就是简单的工厂模式。也叫静态工厂。
利用反射创建工厂对象
还有上面的例子只是把工厂换做基于反射的例子:代码如下:
class ReflexFactory {
public static <T extends Water> T getWater(Class<T> clz) {
T t = null;
try {
t = (T) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return t;
}
}
然后把Demo的工厂换做反射工厂。代码如下:只写了一个,后面略。
运行结果为:
看了网上之后有多种创建工厂的方法下面只列出代码:
多方法工厂模式,需要什么调什么
//多方法工厂模式,需要什么就调什么
class WaterFactory{
public static NongFuWater getNongFuWater() {
return new NongFuWater();
}
public static WaHaWater getWaHaWater() {
return new WaHaWater();
}
public static BaiSuiWater getBaiSuiWater() {
return new BaiSuiWater();
}
}
还有java源码的工厂模式:例如线程池都知道创建线程池子的方法有很多,其中有一种为创建固定数量的线程池方法:
/**创建具一个可重用的,有固定数量的线程池
* 每次提交一个任务就提交一个线程,直到线程达到线城池大小,就不会创建新线程了
* 线程池的大小达到最大后达到稳定不变,如果一个线程异常终止,则会创建新的线程
*/
ExecutorService es=Executors.newFixedThreadPool(2);
for(int i=0;i<10;i++){
ThreadChi tc=new ThreadChi();
es.execute(tc);
}
es.shutdown();