单例模式大家都不陌生,即让一个类只有一个实例。
单例模式分为懒汉式和饿汉式。
懒汉式☞方法调用时再实例化对象,什么时候用什么时候实例化,比较懒。
饿汉式☞方法调用前对象就已经创建好了,比较有捉急。
本文着重描述懒汉式与多线程的内容。
1.饿汉式
public class SingletonHungary {
private static SingletonHungary instance = new SingletonHungary();
private SingletonHungary(){}
public static SingletonHungary getInstance() {
return instance;
}
}
public class Main extends Thread{
@Override
public void run() {
System.out.println(SingletonHungary.getInstance().hashCode());
}
public static void main(String[] args) {
Main[] mts = new Main[10];
for(int i = 0 ; i < mts.length ; i++){
mts[i] = new Main();
}
for (int j = 0; j < mts.length; j++) {
mts[j].start();
}
}
}
由打印出的hashcode相同说明:他们是同一个对象
饿汉式在方法调用之前就创建了静态对象,静态对象是全局唯一的,即使有多个线程访问也是在使用同一个对象。这样的方式是线程不安全的。
2.懒汉式
(懒汉式的实现方式有多种,最终演化出了一种最优的方式,此处只介绍这种最优方式)
public class DubbleSingleton {
private static DubbleSingleton ds;
public static DubbleSingleton getDs(){
if (ds==null){
System.out.println(Thread.currentThread().getName()+":"+"第一轮判断,我的ds==null,我要去抢创建对象的锁啦!");
try {
//模拟初始化对象的准备时间...
System.out.println(Thread.currentThread().getName()+":"+"模拟初始化对象的准备时间...");
Thread.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized (DubbleSingleton.class){
System.out.println(Thread.currentThread().getName()+":"+"第二轮判断,我的ds==null,我来创建对象啦!");
if(ds==null){
System.out.println(Thread.currentThread().getName()+":"+"哈哈哈哈哈!是我创建的对象!");
ds = new DubbleSingleton();
}else {
System.out.println(Thread.currentThread().getName()+":"+"第二轮判断之后,我的ds!=null,看来有人在我之前把对象创建了!");
}
}
}
return ds;
}
public static void main(String[] args){
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
}
},"t3");
t1.start();
t2.start();
t3.start();
}
}
请读者先思考一个问题再继续往下看:
为什么要判断两次ds==null?
————————————————————-思考分割线—————————————————————-
我们来看一下打印结果:
因为懒汉式是方法调用时再创建对象,所以在多线程高并发时为了保证安全,并且高效,在代码块上加了synchronized关键字,多个线程可以同时调用getDS方法,这时他们的ds都是null,但当一个线程在创建一个对象后,由于其他线程已经在第一轮判断时就进入了方法,所以其他线程会继续走synchronized代码块,这时进行第二轮判断,其他线程会在第二轮判断时发现对象已经创建,则不会再继续创建对象。
就酱,OVER!