2.1 概念
- 只有在你需要的时候,进行调用才会实例化。
2.2 示例
下面给大家看下五种不同懒汉模式他们之间的区别。
- 第一种方法:在多线程访问时存在线程问题
首先可以先把线程休眠哪里的代码去掉,去执行该代码是基本上没有出现线程问题,因为在绝对的速度面前线程是安全的。
但是将线程睡眠时间加上,那么就会出现线程问题,所以我们就可以用到另外一种。
-
package ;
-
/**
-
* 单例懒汉模式:有线程问题
-
* @author zjjt
-
*
-
*/
-
public class SingletonDemo03 {
-
-
//懒汉模式
-
-
-
//先私有化构造函数,无法实例化出来
-
private SingletonDemo03() {
-
//线程休眠
-
try {
-
(100);
-
} catch (Exception e) {
-
();
-
}
-
}
-
-
private static SingletonDemo03 dm02=null;
-
-
//我们解决这种问题就使用到synchronized
-
//因为他只允许一个线程先执行,必须要等该线程执行完,后面的才能执行。
-
-
public static synchronized SingletonDemo03 getInstance() {
-
//判断如果该类还为null,那么就实例化,如果不为空,就直接返回该。
-
if(dm02==null) {
-
dm02=new SingletonDemo03();
-
}
-
return dm02;
-
}
-
-
//测试:
-
public static void main(String[] args) {
-
for (int i = 0; i < 100; i++) {
-
new Thread(()->{
-
(().hashCode());
-
}).start();
-
}
-
}
-
-
}
线程问题:
- 第二种方法:给它加上同步锁(synchronized)
利:
解决了多线程的安全问题。
弊:
降低了程序的性能。每个线程都要去判断锁机制,那么会增加程序运行的负担,同时只要做判断,CPU都要处理,那么也会消耗CPU的资源。即就是加同步会降低程序
的性能。
-
package ;
-
/**
-
* 单例懒汉模式
-
* @author zjjt
-
*
-
*/
-
public class SingletonDemo03 {
-
-
//懒汉模式
-
-
-
//先私有化构造函数,无法实例化出来
-
private SingletonDemo03() {
-
try {
-
(100);
-
} catch (Exception e) {
-
();
-
}
-
}
-
-
private static SingletonDemo03 dm03=null;
-
-
//我们解决这种问题就使用到synchronized
-
//因为他只允许一个线程先执行,必须要等该线程执行完,后面的才能执行。
-
-
public static synchronized SingletonDemo03 getInstance() {
-
//判断如果该类还为null,那么就实例化,如果不为空,就直接返回该。
-
if(dm03==null) {
-
dm03=new SingletonDemo03();
-
}
-
return dm03;
-
}
-
-
//测试:
-
public static void main(String[] args) {
-
for (int i = 0; i < 100; i++) {
-
new Thread(()->{
-
(().hashCode());
-
}).start();
-
}
-
}
-
-
}
- 第三种方法:不在方法上使用同步锁,在方法里面使用同步锁
利:
性能比第二种方法好。因为当一个线程进入同步锁里面,判断完以后,实例化,后面的线程之间返回该对象就可以啦!!!
弊:
也是最为致命的地方在于在我们判断是否为空的时候,假如第一个线程刚好走到判断是否为空和同步锁中间,我在下面代码标注//-----这块位置,突然被第二个线程抢了,于是第二个线程先进了同步锁,判断完了实例化了,然后由于第一个线程已经做完非空判断了,他也会走到同步锁里面,这样子又实例化了,所有这种方法也是存在多线程问题。
-
package ;
-
/**
-
* 单例懒汉模式:有线程问题
-
* @author zjjt
-
*
-
*/
-
public class SingletonDemo04 {
-
-
//懒汉模式
-
-
-
//先私有化构造函数,无法实例化出来
-
private SingletonDemo04() {
-
try {
-
(100);
-
} catch (Exception e) {
-
();
-
}
-
}
-
-
private static SingletonDemo04 dm04=null;
-
-
//我们解决这种问题就使用到synchronized(同步锁)
-
//因为他只允许一个线程先执行,必须要等该线程执行完,后面的才能执行。
-
-
-
public static SingletonDemo04 getInstance() {
-
//判断如果该类还为null,那么就实例化,如果不为空,就直接返回该。
-
if(dm04==null) {
-
//--------
-
synchronized () {
-
dm04=new SingletonDemo04();
-
}
-
}
-
-
return dm04;
-
}
-
-
//测试:
-
public static void main(String[] args) {
-
for (int i = 0; i < 100; i++) {
-
new Thread(()->{
-
(().hashCode());
-
}).start();
-
}
-
}
-
-
}
- 第四种方法:双重判断
既保证了性能更加好一些,又保证了多线程的安全。
流程:
第一个线程----->第一个判断是否为空---->第二个线程抢了----->进入同步锁---->判断完以后出来实例化完成---->第一个线程才在进入同步锁--->进入第二个判断----->而对象不为空了----->不会进入第二个判断里面,直接返回对象。
-
package ;
-
/**
-
* 双重判断
-
* @author zjjt
-
*
-
*/
-
public class SingletonDemo05 {
-
-
-
-
//先私有化构造函数,无法实例化出来
-
private SingletonDemo05() {
-
try {
-
(100);
-
} catch (Exception e) {
-
();
-
}
-
}
-
-
private static SingletonDemo05 dm02=null;
-
-
//我们解决这种问题就使用到synchronized(同步锁)
-
//因为他只允许一个线程先执行,必须要等该线程执行完,后面的才能执行。
-
-
-
public static SingletonDemo05 getInstance() {
-
//判断如果该类还为null,那么就实例化,如果不为空,就直接返回该。
-
if(dm02==null) {
-
//双重判断,当线程走到这里停止,换成了另外一个线程,但是他往下走还是会在判断一遍,所以这种方法更加安全。
-
synchronized () {
-
if(dm02==null) {
-
dm02=new SingletonDemo05();
-
}
-
}
-
}
-
-
return dm02;
-
}
-
-
//测试:
-
public static void main(String[] args) {
-
for (int i = 0; i < 100; i++) {
-
new Thread(()->{
-
(().hashCode());
-
}).start();
-
}
-
}
-
-
}
- 第五种方法: 静态内部类方法
我们在该方法中创建一个静态内部类,然后在该静态内部类中实例化SingletonDemo07,
我们在测试的时候调用getInstance() 方法,就会执行静态内部类,而静态内部类哪里加上了static,所以只会实例一次。
-
package ;
-
/**
-
* 静态内部类
-
* @author zjjt
-
*
-
*/
-
public class SingletonDemo07 {
-
-
-
-
//先私有化构造函数,无法实例化出来
-
private SingletonDemo07() {
-
try {
-
(100);
-
} catch (Exception e) {
-
();
-
}
-
}
-
-
-
-
//静态内部类,只有当我们调用他时,他才会执行。
-
-
public static class SingletonDemoHolder{
-
private final static SingletonDemo07 dm07=new SingletonDemo07();
-
}
-
-
-
public static SingletonDemo07 getInstance() {
-
return SingletonDemoHolder.dm07;
-
}
-
-
-
-
-
//测试:
-
public static void main(String[] args) {
-
for (int i = 0; i < 100; i++) {
-
new Thread(()->{
-
(().hashCode());
-
}).start();
-
}
-
}
-
-
}
注:以上这五种方法都存在一个问题,就是虽然构造函数被私有化了,但是可以通过反射破话单例的私有化构造函数,所以我们可以使用第六种
- 第六种方法:使用枚举型enum
枚举型是由java之父创建的,枚举型里面压根没有构造函数,所以无法实例出该类。是jvm帮我们初始化的,反射机制也无法破话,因为枚举型没有构造函数。
-
package ;
-
/**
-
* 枚举类
-
* @author zjjt
-
*
-
*/
-
public enum SingletonDemo06 {
-
-
-
//枚举型是由java之父创建的,枚举型里面压根没有构造函数,所以无法实例出该类。
-
//是jvm帮我们初始化的
-
//反射机制也无法,因为枚举型没有构造函数
-
-
INSTANCE;
-
-
public String hello(String name) {
-
return "hello " + name;
-
}
-
-
-
//测试
-
public static void main(String[] args) {
-
for (int i = 0; i < 100; i++) {
-
new Thread(()->{
-
(());
-
}).start();
-
}
-
}
-
}
-
-
-