01单例模式
用啥方式解决实际问题更合适就用啥方式,我们不追求那些不必要的完美
饿汉模式01 最简单版但是好用
package xzc._01singleton;
/*
-
饿汉模式01
-
Class.forName() 也行啊
-
1.放单例的静态变量
-
2.构造方法私有化
-
3.取出单例的静态化方法
-
- 类加载到内存后就会自动实例化一个单例
-
*/
public class _01hungry {public static final _01hungry INSTANCE = new _01hungry();
private _01hungry() {};
public static _01hungry getInstance(){
return INSTANCE;
}public void sayHello(){
System.out.println(“01hungry”);
}public static void main(String[] args) {
// _01hungry hungry01 = new _01hungry();
// _01hungry hungry02 = new _01hungry();
_01hungry hungry01 = _01hungry.getInstance();
_01hungry hungry02 = _01hungry.getInstance();
System.out.println(hungry01==hungry02);
}
}
饿汉模式02 静态代码块
package xzc._01singleton;
/*
-
饿汉模式02 静态代码块
-
Class.forName() 也行啊 暂时放弃
-
1.放单例的静态变量
-
2.构造方法私有化
-
3.取出单例的静态化方法
-
- 类加载到内存后就会自动实例化一个单例
-
不管用到与否 类加载就实例化了
-
*/
public class _02hungry {private static final _02hungry INSTANCE;
/*- 执行顺序:
1、类内容(静态变量、静态初始化块) => 实例内容(变量、初始化块、构造器)
2、父类的(静态变量、静态初始化块)=> 子类的(静态变量、静态初始化块)=> 父类的(变量、初始化块、构造器)=> 子类的(变量、初始化块、构造器)
3、(静态)变量和(静态)代码块的也是有执行顺序的,与代码书写的顺序一致。在(静态)代码块中可以使用(静态)变量,但是被使用的(静态)变量必须在(静态)代码块前面声明。
*
* - */
static {
INSTANCE = new _02hungry();
}
private _02hungry() {};
public static _02hungry getInstance(){
return INSTANCE;
}
public void sayHello(){
System.out.println(“02hungry”);
}public static void main(String[] args) {
// _01hungry hungry01 = new _01hungry();
// _01hungry hungry02 = new _01hungry();
_01hungry hungry01 = _01hungry.getInstance();
_01hungry hungry02 = _01hungry.getInstance();
System.out.println(hungry01==hungry02);
}
} - 执行顺序:
懒汉模式01 最简线程不安全版
顺带复习了线程创建的方式 和lamda 函数
package xzc._01singleton;
/*
** 懒汉模式
-
- 1.定义放单例的静态变量
-
2.构造方法私有化
-
3.取出单例的静态化方法
-
解决了自动创建的问题 反而带来了线程这个更大的的问题
-
*/
public class _03LazySingleton {
private static _03LazySingleton INSTANCE;private _03LazySingleton() {
}public static _03LazySingleton getInstance(){
if (INSTANCE ==null){try { Thread.sleep(10); }catch (InterruptedException e){ e.printStackTrace(); } INSTANCE = new _03LazySingleton(); } return INSTANCE;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i<50; i++){
// 1 继承 Thread重写runable方法
// MyThread myThread = new MyThread();
// myThread.sleep(i);
// myThread.start();
// 2.1实现runnable方法
// Thread myThread = new Thread(new MyThreadRunnable());
// myThread.start();
// 2.2实现runnable方法 匿名内部类 拿来当参数的类,只需要使用一次
// 匿名内部类也就是没有名字的内部类
// 正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写
// 但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
// new Thread(new MyThreadRunnable(){
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName() +“xxxx”+ _03LazySingleton.getInstance().hashCode());
// }
// }).start();
// 2.3实现runnable方法 匿名内部类 加lamda方法
// Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
// Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
// 使用 Lambda 表达式可以使代码变的更加简洁紧凑
// 这里只是实现了了一个run方法可以更加简写 为了更加清楚 这里cv2.2代码来简写
// new Thread(new MyThreadRunnable(){
new Thread(()->{
// @Override
// public void run() {
System.out.println(Thread.currentThread().getName() +"—"+ _03LazySingleton.getInstance().hashCode());
// }
}).start();
}
}
}
///1/
//class MyThread extends Thread {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName() +"—"+ _03LazySingleton.getInstance().hashCode());
// }
//}
///2.1/
//class MyThreadRunnable implements Runnable {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName() +"—"+ _03LazySingleton.getInstance().hashCode());
// }
//}
懒汉模式02 synchronized 加instance方法版
package xzc._01singleton;
/*
** 懒汉模式 加synchronized版
-
- 1.定义放单例的静态变量
-
2.构造方法私有化
-
3.取出单例的静态化方法
-
解决了自动创建的问题 反而带来了线程这个更大的的问题
-
解决这个问题 加锁 只有一行 再原来的基础上多加个 synchronized 去修饰 getInstance方法
-
问题解决了 但是代价是性能降低了
-
*/
public class _04LazySingletonSynchronized {
private static _04LazySingletonSynchronized INSTANCE;private _04LazySingletonSynchronized() {
}public static synchronized _04LazySingletonSynchronized getInstance(){
if (INSTANCE ==null){try { Thread.sleep(10); }catch (InterruptedException e){ e.printStackTrace(); } INSTANCE = new _04LazySingletonSynchronized(); } return INSTANCE;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i<50; i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName() +"—"+ _04LazySingletonSynchronized.getInstance().hashCode());
}).start();}
}
}
懒汉模式03 synchronized兼顾安全与性能双重校验锁
package xzc._01singleton;
/*
*
-
解决了自动创建的问题 反而带来了线程这个更大的的问题
-
解决这个问题 加锁 只有一行 再原来的基础上多加个 synchronized 去修饰 getInstance方法
-
问题解决了 但是代价是性能降低了
-
性能降低了很大的原因是锁的范围太大了 那就减少锁的范围吧 把锁精确放到代码块–不够精确的话···锁没用或者耗费更多的性能在锁上
-
因为这里没用 因为if判断和 和锁里面的代码块没有一体化
-
做个双重检查吧
-
为啥判断两次?
-
第一次要判断 看起来多了一个判断的性能消耗 但是要考虑到大多数情况下都是null直接返回,可以省出来上锁解锁的消耗 其实是一种优化。
-
第二次判断保证单例
-
*/
public class _05LazySingletonSynchronized02 {
private static _05LazySingletonSynchronized02 INSTANCE;private _05LazySingletonSynchronized02() {
}public static _05LazySingletonSynchronized02 getInstance(){
// if (INSTANCE ==null){
synchronized(_05LazySingletonSynchronized02.class){
if (INSTANCE ==null){//里面再重新检查一遍
try {
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
INSTANCE = new _05LazySingletonSynchronized02();
}
}
// }
return INSTANCE;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i<50; i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName() +"---"+ _05LazySingletonSynchronized02.getInstance().hashCode());
}).start();
}
}
}
静态内部类版 (最完美版 比01好)
package xzc._01singleton;
/*
- 利用了classloader的机制来保证初始化instance时只有一个线程,(虚拟机加载classloader只会加载一次)
- 所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。
- 最完美的写法
- */
public class _06LazyStaticInnerClass{
private _06LazyStaticInnerClass(){}
private static final _06LazyStaticInnerClass INSTANCE=new _06LazyStaticInnerClass();
public static _06LazyStaticInnerClass getInstance(){
return _06LazyStaticInnerClass.INSTANCE;
}
}
终极版 枚举(java作者推荐)
package xzc._01singleton;
public enum _07SingletonByEnum {
INSTANCE;
public void m(){
System.out.println(“完美中的完美实现单例 不仅解决单例 还解决反序列化”);
}
public static void main(String[] args) {
for (int i = 0; i<50; i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName() +"—"+ _07SingletonByEnum.INSTANCE.hashCode() +"—");
// _07SingletonByEnum.INSTANCE.m();
}).start();
}
}
}
在spring的xml配置bean中值得注意的地方
1)当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2)当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了