马士兵老师计算机网络笔记1:单例模式讨论

单例模式是指不允许用户掉构造方法自己创建,通过静态方法获取对象,且获取的是同一个实例。
case1:

public class DCLDemoCase1 {
    
    
	private static DCLDemoCase1 instance=new DCLDemoCase1();
	private DCLDemoCase1() {
    
    };
	public static DCLDemoCase1 getInstance() {
    
    
		return instance;
	}
	private int m=1;
	public void m() {
    
    
		System.out.println(m);
	}
}
public static void main(String[] args) {
    
    
		DCLDemoCase1 instance2 = DCLDemoCase1.getInstance();
		DCLDemoCase1 instance3 = DCLDemoCase1.getInstance();

		System.out.println(instance2==instance3);
	}

测试结果:true。两个对象相同。
case2:
在语句private static DCLDemoCase1 instance=new DCLDemoCase1();中新创建的这个对象还没被用上,上来直接创建这个对象,有时候创建一个对象比较浪费时间,能不能把创建的这个过程放在一个方法里需要的时候再创建。
于是就有了第二种写法:

public class DCLDemoCase2 {
    
    
	private static DCLDemoCase2 instance;
	private int m=1;
	private DCLDemoCase2() {
    
    };
	public static synchronized DCLDemoCase2 getInstance() {
    
    
		if(instance==null) {
    
    
			try {
    
    
				Thread.sleep(1);
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
			}
			instance=new DCLDemoCase2();
		}
		return instance;
	}
		public void m() {
    
    
		System.out.println(m);
	}
	}

多线程情况下测试能达到运行效果,都是同一个对象。
插曲:
但也发现:确实在缺少Thread.sleep(1)休眠下:

public class DCLDemoCase2 {
    
    
	private static DCLDemoCase2 instance;
	private int m=1;
	private DCLDemoCase2() {
    
    };
	public static DCLDemoCase2 getInstance() {
    
    
		if(instance==null) {
    
    
			instance=new DCLDemoCase2();
		}
		return instance;
	}
		public void m() {
    
    
		System.out.println(m);
	}
	}

也会达到预期效果,理论上会出现不一样的对象,可能确实在这种情况下不会有不同线程同时进入到if里创建出不同的对象。

接着case2
case3:
但是这种直接把整个方法全锁了,这种锁太粗了,在锁的优化中,有一种就是锁细化。

public class DCLDemoCase2 {
    
    
	private static DCLDemoCase2 instance;
	private int m=1;

	private DCLDemoCase2() {
    
    
	};

	public static DCLDemoCase2 getInstance() {
    
    
		if (instance == null) {
    
    
			synchronized (DCLDemoCase1.class) {
    
    
				instance = new DCLDemoCase2();
			}
		}
		return instance;
	}
		public void m() {
    
    
		System.out.println(m);
	}
	}

多线程下发现会出现不同对象,达不到单线程的效果。
这是因为:初始时每个线程的初始instance=null,所以会有数个线程进入到if(instance==null)里,于是他们会得到不同的对象。
case4:
双重锁dcl

public class DCLDemoCase4 {
    
    
	private static DCLDemoCase4 instance;
private int m=1;
	private DCLDemoCase4() {
    
    
	};

	public static synchronized DCLDemoCase4 getInstance() {
    
    
		if (instance == null) {
    
    
			synchronized (DCLDemoCase1.class) {
    
    
				if(instance==null) {
    
    
				instance = new DCLDemoCase4();
				}
			}
		}
		return instance;
	}
		public void m() {
    
    
		System.out.println(m);
	}
	}

当有多个线程进入第一次判空后,那么就算第一个线程创建好对象,instance不为空,这些线程就有资格进入锁(synchronized)中创建对象,但当第一个线程进入锁中创建好对象,已经给instance赋值,instance不为空,其他线程(进入第一次判空除了第一个线程)只能进入到锁中,但无法逾越第二次判空,无法创建新线程。
case5:
程序显得完美无缺,但是instance需要volatile修饰吗?
确实需要。
在这里插入图片描述
创建一个对象大概经过3步(只是大概):)(1)在堆中开辟一块内存【new】(2)构造方法…初始化【invokespecial】(3)对象变量指向这块内存【astore】

**那么可能会有这种情况:**指令重排序
在线程1开辟好内存,类中m的默认值为0,然后直接赋值给类对象t,有两个线程,第一个线程先过来发生指令重排序,没来的及给m赋值,第二个线程过来判断t不为空直接使用,第二个线程拿到的是不正确的对象实例。

package doubleCheckLock;

public class DCLDemoCase5 {
    
    
	private static volatile DCLDemoCase5 instance;
	private int m=1;
	private DCLDemoCase5() {
    
    
	};

	public static synchronized DCLDemoCase5 getInstance() {
    
    
		if (instance == null) {
    
    
			synchronized (DCLDemoCase1.class) {
    
    
				if(instance==null) {
    
    
				instance = new DCLDemoCase5();
				}
			}
		}
		return instance;
	}
	public void m() {
    
    
		System.out.println(m);
	}

	public static void main(String[] args) {
    
    
		for (int i = 0; i < 100; i++) {
    
    
			new Thread(() -> {
    
    
				System.out.println(DCLDemoCase2.getInstance().hashCode());
			}).start();
		}
	}
}

在这里volatile表示禁止指令重排序。

猜你喜欢

转载自blog.csdn.net/qq_52605986/article/details/117306059