单例设计模式
它是解决某类问题的一套有效的方案(模版)。
一、单例代码书写步骤:
1、私有本类所有的构造方法;
2、创建本类的对象;
3、对外提供一个静态的方法来获取对象。
二、单例常用的两种书写格式:
1、饿汉式:
class Single{
// 私有所有构造方法
private Single() {}
// 在类中创建自己的对象
private static Single instance = new Single();
// 对外提供get方法,获取类中唯一的对象
public static Single getInstance() {
return instance;
}
}
2、懒汉式:(常面)
class Single{
// 私有构造方法
private Single(){}
// 只定义变量,不创建对象
private static Single instance = null;
// 对外提供方法,获取本类对象
public static Single getInstance() {
// 不要直接返回,先判断instance中有没有对象
if( instance == null ) {
// 判断成立,说明还没有唯一的对象,则创建对象
instance = new Single();
}
// 有则直接返回该对象
return instance;
}
}
三、常面问题:
1、单例解决的问题是什么?
保证当前的对象在程序中只有一个(对象唯一)。
2、单例模式方法上为什么要加静态?
添加静态的目的是保证外界可以通过类名调用该方法,从而拿到本类的唯一的那个对象。
因为这个类以外的地方没有办法创建类的对象,如果 getInstance 方法不是静态的,导致类以外的别的程序根本就无法获取到这个类的对象了,只有将 getInstance 方法静态了,外界才能通过唯一的这个方法来得到这个类的唯一的那个对象。
3、懒汉式在多线程操作的时候有没有安全问题?
有,在 getInstance 方法中的判断和赋值两行代码上操作共享的 instance 变量,这样在操作的过程中CPU会随机切换,可能出现安全问题。
解决方案:在判断和赋值代码上添加同步代码块。
4、添加同步时效率会降低,能不能优化?
可以。如下所示:
class Single{
private Single() {}
private static Single instance = null;
/* a.可以定义一个对象作为同步的锁对象—-> synchronized(lock)
* private static Object lock = new Object();
* b.因为是静态同步的方法,所以还可以使用格式:synchronized(Single.class)
*
* 1、此处添加静态的目的是保证外界可以通过类名调用方法名,从而拿到本类的唯一的那个对象
* 2、同步(synchronized)不能添加在此处方法上,会影响后续线程获取唯一对象的效率
*/
public static Single getInstance() {
/* 3、外面的判断目的是为了后续其他线程在获取对象时提供效率
* 当后续其他线程再调用getInstance方法时,由于instance变量中已经有对象了,
* 则判断不成立,线程就不用再进入到同步中,更不会判断同步的锁了,从而效率提高
*/
if(instance == null) {
// 4、此处判断和赋值有安全问题,所以添加同步,保证多个线程只有一个线程能够创建对象
synchronized ( Single.class ) {
if(instance == null) {
instance = new Single();
}
}
}
return instance;
}
}
//书写线程的任务类
class Demo implements Runnable{
public void run() {
System.out.println(Single.getInstance());
}
}
//书写一个测试类
public class SingleTest {
public static void main(String[] args) {
//new一个实例对象
Demo t = new Demo();
//创建线程并启动
new Thread(t).start();;
new Thread(t).start();;
}
}
5、能不能将 synchronized 添加到 run 方法上,为什么?
按照语法规则,是可以在run方法上书写同步的。
但是在实际开发中是不可能在run方法上添加同步的。同步方法在线程执行的时候,每个线程要进入这个方法都需要获取同步锁,如果获取不到锁,线程就无法去执行这方法。而run方法是线程要执行的任务方法,如果线程都进不去run方法,相当于线程根本就没有拿到自己的任务。