//饿汉式单列
public class Hungry {
//构造私有化是单列的第一步
private Hungry(){
}
private final static Hungry HUNGRY=new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
这种模式上来就加载 ,如果我们在加几个属性,会发现data1这些数组及其的耗费内存资源。
//饿汉式单列
public class Hungry {
private byte[] data1=new byte[1024*1024];
private byte[] data2=new byte[1024*1024];
private byte[] data3=new byte[1024*1024];
private byte[] data4=new byte[1024*1024];
//构造私有化是单列的第一步
private Hungry(){
}
private final static Hungry HUNGRY=new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
所以我们可以使用懒汉式
二、懒汉式当真正使用的时候才去加载
public class LayzMan {
private LayzMan(){ } //构造器私有
private static LayzMan layzMan;
public static LayzMan getInstance(){
if(layzMan==null){
layzMan=new LayzMan();
}
return layzMan;
}
}
但是这种单列只有在单线程下是ok的,如果在多线程下:
public class LayzMan {
private LayzMan(){
System.out.println(Thread.currentThread().getName()+"ok");
} //构造器私有
private static LayzMan layzMan;
public static LayzMan getInstance(){
if(layzMan==null){
layzMan=new LayzMan();
}
return layzMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LayzMan.getInstance();
}).start();
}
}
}
//第一次打印结果
Thread-1ok
Thread-2ok
Thread-0ok
//第二次
Thread-0ok
会发现如果是多线程的情况下每次都不一样。有时会new多次
所以我们为了保证多线程情况下单列也能被使用,就需要加锁
public class LayzMan {
private LayzMan(){
System.out.println(Thread.currentThread().getName()+"ok");
} //构造器私有
private static LayzMan layzMan;
//双重检测锁模式,DCL懒汉式单例
public static LayzMan getInstance(){
if(layzMan==null){
synchronized (LayzMan.class){
if(layzMan==null){
layzMan=new LayzMan();
}
}
}
return layzMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LayzMan.getInstance();
}).start();
}
}
}
//打印多次都会只会出现
Thread-0ok
这种方式简称为DCL懒汉式,但是这种方式也不安全
public static LayzMan getInstance(){
if(layzMan==null){
synchronized (LayzMan.class){
if(layzMan==null){
layzMan=new LayzMan();
/*
* new LayzMan();不是原子性 *** 作
* 底层 *** 作步骤分别是
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 正常情况下是 1 2 3按照步骤执行
* 但也可能出现 132 的情况 就是先走3
* 如果A线程走的是132 当A还没执行2时
* ,B线程就 以为A构造完了直接走了下面的return
*
* */
}
}
}
return layzMan;
}
所以 我们一定得保证构建的顺序性,需要在被构建的对象上加 volatile关键字
private volatile static LayzMan layzMan;
完整的DCL懒汉式
public class LayzMan {
private LayzMan(){
System.out.println(Thread.currentThread().getName()+"ok");
} //构造器私有
private volatile static LayzMan layzMan; //一定记得加上volatile 关键字
public static LayzMan getInstance(){
if(layzMan==null){
synchronized (LayzMan.class){
if(layzMan==null){
layzMan=new LayzMan();
}
}
}
return layzMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LayzMan.getInstance();
}).start();
}
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)