6.单例模式

1.什么是单例模式?

确保一个类只有一个实例,并提供一个全局访问点。
这句话什么意思呢? 就是利用这个设计模式可以让指定的类在它的整个使用周期上只生产出一个实例对象并存放在内部,且提供一个公共方法去访问这个对象。

2.根据具体的例子理解单例模式

2.1 单例类设计

员工A在写代码的时候发现自己写一个了类,但是这个类在整个系统中好像只需要一个对象,从业务的角度上,这个类不允许出现第二个对象,不然会产生很恐怖的后果!! 但是他又没办法防止别人使用这个类生成对象,他这时候犯难了,他不知道该怎么办。
员工B听到他的难处了,轻笑一声:还是太年轻了吧! 你不知道有一种设计叫单例模式吗?

先上图:
在这里插入图片描述

确保一个类只有一个实例(构造方法私有,声明静态自类型变量),并提供一个全局访问点(提供一个公共方法访问静态变量)。

没错,就是这么简单,下面上代码:

这个类保证了自己只生成一个对象,怎么做到的呢? 其实将构造方法私有化,并在自己内部组合一个自身的引用且提供一个公共方法得到这个引用。

package SingletonPattern.first;

/**
 * 普通单例类,应用场景:
 * 注册表设置,连接池,线程池等等。利用单件模式对象,我们可以确保程序中使用的全局资源只有一份
 */
public class Singleton {
    private static Singleton instance;
    private Singleton(){}
    /**
     * 延迟实例化
     * @return
     */
    public static Singleton getInstance(){
        if(instance==null){
            instance = new Singleton();
        }
        return instance;
    }
    public void sayHello(){
        System.out.println("Hello world!!");
    }
}

员工A如获至宝,立即打断了员工B的讲解,并迫不及待的在程序中用上了。 这时员工B只是笑笑不说话。

2.2 多线程下的单例类安全吗?

用了一段时间,员工A发现,上面单例类的设计存在一个缺陷,就是在多线程的情况下不安全!!
如果两个线程同时进入了空判断,这时,可能会发生实例化两次的情况。
如下所示:

package SingletonPattern.third;

/**
 * 单例模式确保了一个类只有一个实例,并提供了一个全局访问点
 */
public class SingletonChocolateBoiler {
    public static SingletonChocolateBoiler Instance;

    /**
     * 通过私有化构造方法,达到单一实例的效果
     */
    private SingletonChocolateBoiler(){
    }

    /**
     * 获取实例唯一入口
     * @return 此类的唯一实例
     */
    public static SingletonChocolateBoiler getInstance(){
        if(Instance==null){
            Instance = new SingletonChocolateBoiler();
            System.out.println("构造了一个新SingletonChocolateBoiler对象");
        }
        return Instance;
    }

}

线程安全测试:

package SingletonPattern.third;
/**
 * 按道理说,使用Singleton对象时,无论多少个线程使用它,它都应该始终构造一次即可,但是,
 * 如果按照second中的做法去设计类,时会出现线程安全问题的,例如如下
 */
public class ThreadProblem {
    public static void main(String[] args) {
        for(int i=0;i<1000;i++){
            new Thread(()->{
                SingletonChocolateBoiler.getInstance();
            }).start();
        }
    }
}

在这里插入图片描述

这时,员工A想起了员工B在向他介绍这个模式的时候好像还有些话没说。于是,他找到了员工B,员工B欣慰的笑了,说:小子,你终于发现了。 给你个提示: synchronized
员工A茅塞顿开:

package SingletonPattern.third;

public class FirstSafeSingleton {
    
    private static FirstSafeSingleton Instance;

    private FirstSafeSingleton(){}
    /**
     * 通过这样进行同步确实可以解决问题,但是通过同步加锁是一定会增加性能开销的
     * 因为我们只需要在第一次构造实例时进行同步即可,其他的情况不需要同步
     * @return
     */
    public static synchronized FirstSafeSingleton getInstance(){
        if(Instance == null){
            Instance = new FirstSafeSingleton();
            System.out.println("构造一个新的FirstSafeSingleton");
        }
        return Instance;
    }
}

紧接着做出了如下对比测试:

package SingletonPattern.third;

/**
 * 按道理说,使用Singleton对象时,无论多少个线程使用它,它都应该始终构造一次即可,但是,
 * 如果按照second中的做法去设计类,时会出现线程安全问题的,例如如下
 */
public class ThreadProblem {
    public static void main(String[] args) {

        for(int i=0;i<10000;i++){
            new Thread(()->{
                SingletonChocolateBoiler.getInstance();
            }).start();
        }

        //通过synchronized解决后测试
        for(int i=0;i<10000;i++){
            new Thread(()->{
                FirstSafeSingleton.getInstance();
            }).start();
        }
    }
}

在这里插入图片描述

果然,安全问题解决了。但是员工A还是觉得不划算,只是为了解决第一次构造实例时的线程安全问题就牺牲整个getInstance方法的性能,太不划算了。
有没有什么方法,只保证在第一次构造对象时的线程安全即可。

员工B欣慰的笑了,随后提示到: 双重检测机制。

这时员工A上百度搜索了一下,茅塞顿开:

package SingletonPattern.third;

public class SecondSafeSingleton {
    /**
    *volatile保证了此变量的可见性(一个线程对主内存的修改可以及时的被其他线程观察到)
    *这样当第一个线程将此变量的实例存入主内存后,其他线程知道了后
    *在进行Instance==null判断的时候就会判断正确并获得正确的实例对象
    * 防止重排序出现问题
    */
    private volatile static SecondSafeSingleton Instance;
    private SecondSafeSingleton(){ }
    /**
     * 双重检查锁机制解决first中引起的性能降低
     * @return
     */
    public static SecondSafeSingleton getInstance(){
        //第一次检查,这里可能会出现多个线程同时进入的情况
        if(Instance==null){
            //此时采取同步措施,保证多个线程进入下面的代码块是串行的
            synchronized (SecondSafeSingleton.class){
                //这里第一个线程肯定是满足条件的,此时它进入下面的步骤进行实例构造
                //但由于我们使用了volatile,Instance被更新了立刻被其他线程知道了,
                //第二个线程在进来的时候这里的判断是过不去的
                if(Instance==null){
                    Instance = new SecondSafeSingleton();
                    System.out.println("构造了一个新的SecondSafeSingleton");
                }
            }
        }
        return Instance;
    }
}

然后又做出了对比测试:
在这里插入图片描述

代码仓库地址如下:
https://gitee.com/yan-jiadou/design-mode/tree/master/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/src/main/java/SingletonPattern/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,这个是一个比较复杂的音乐播放器项目,需要用到多个设计模式,下面是具体的代码实现: 1. 工厂方法模式: ```java public interface MusicFactory { Music createMusic(); } public class Mp3Factory implements MusicFactory { @Override public Music createMusic() { return new Mp3Music(); } } public class WavFactory implements MusicFactory { @Override public Music createMusic() { return new WavMusic(); } } public class FlacFactory implements MusicFactory { @Override public Music createMusic() { return new FlacMusic(); } } ``` 2. 单例模式: ```java public class MusicPlayer { private static MusicPlayer instance; private MusicPlayer() { } public static MusicPlayer getInstance() { if(instance == null) { instance = new MusicPlayer(); } return instance; } } ``` 3. 命令模式: ```java public interface Command { void execute(); } public class PlayCommand implements Command { private MusicPlayer player; public PlayCommand(MusicPlayer player) { this.player = player; } @Override public void execute() { player.play(); } } public class PauseCommand implements Command { private MusicPlayer player; public PauseCommand(MusicPlayer player) { this.player = player; } @Override public void execute() { player.pause(); } } public class StopCommand implements Command { private MusicPlayer player; public StopCommand(MusicPlayer player) { this.player = player; } @Override public void execute() { player.stop(); } } ``` 4. 装饰器模式: ```java public abstract class MusicDecorator implements Music { private Music music; public MusicDecorator(Music music) { this.music = music; } @Override public void play() { music.play(); } } public class LyricDecorator extends MusicDecorator { public LyricDecorator(Music music) { super(music); } @Override public void play() { super.play(); System.out.println("正在显示歌词..."); } } public class SpeedDecorator extends MusicDecorator { public SpeedDecorator(Music music) { super(music); } @Override public void play() { super.play(); System.out.println("正在调节播放速度..."); } } ``` 5. 状态模式: ```java public interface MusicState { void play(); void pause(); void stop(); } public class PlayState implements MusicState { private MusicPlayer player; public PlayState(MusicPlayer player) { this.player = player; } @Override public void play() { System.out.println("音乐正在播放中..."); } @Override public void pause() { player.setState(new PauseState(player)); System.out.println("音乐已暂停..."); } @Override public void stop() { player.setState(new StopState(player)); System.out.println("音乐已停止..."); } } public class PauseState implements MusicState { private MusicPlayer player; public PauseState(MusicPlayer player) { this.player = player; } @Override public void play() { player.setState(new PlayState(player)); System.out.println("音乐正在继续播放..."); } @Override public void pause() { System.out.println("音乐已暂停..."); } @Override public void stop() { player.setState(new StopState(player)); System.out.println("音乐已停止..."); } } public class StopState implements MusicState { private MusicPlayer player; public StopState(MusicPlayer player) { this.player = player; } @Override public void play() { player.setState(new PlayState(player)); System.out.println("开始播放音乐..."); } @Override public void pause() { System.out.println("音乐未开始播放,无法暂停!"); } @Override public void stop() { System.out.println("音乐未开始播放,无法停止!"); } } ``` 6. 外观模式: ```java public class MusicPlayerFacade { private MusicPlayer player; private MusicList musicList; private MusicState playState; public MusicPlayerFacade() { player = MusicPlayer.getInstance(); musicList = new MusicList(); playState = new StopState(player); } public void play() { playState.play(); } public void pause() { playState.pause(); } public void stop() { playState.stop(); } public void next() { musicList.next(); } public void prev() { musicList.prev(); } public void setPlayState(MusicState state) { this.playState = state; } } public class MusicList { private List<Music> musicList; private Iterator<Music> iterator; private int currentIndex; public MusicList() { musicList = new ArrayList<>(); iterator = musicList.iterator(); currentIndex = -1; } public void addMusic(Music music) { musicList.add(music); } public void next() { if(iterator.hasNext()) { currentIndex++; Music music = iterator.next(); music.play(); } } public void prev() { if(iterator.hasPrevious()) { currentIndex--; Music music = iterator.previous(); music.play(); } } } ``` 7. 策略模式: ```java public interface PlayStrategy { void play(MusicList musicList); } public class SequencePlayStrategy implements PlayStrategy { @Override public void play(MusicList musicList) { while(musicList.hasNext()) { musicList.next(); } } } public class RandomPlayStrategy implements PlayStrategy { @Override public void play(MusicList musicList) { Random random = new Random(); int index = random.nextInt(musicList.size()); musicList.get(index).play(); } } public class LoopPlayStrategy implements PlayStrategy { @Override public void play(MusicList musicList) { while(true) { if(!musicList.hasNext()) { musicList.reset(); } musicList.next(); } } } ``` 8. 迭代器模式: ```java public interface MusicListIterator { boolean hasNext(); Music next(); boolean hasPrevious(); Music previous(); } public class MusicListIteratorImpl implements MusicListIterator { private MusicList musicList; private int currentIndex; public MusicListIteratorImpl(MusicList musicList) { this.musicList = musicList; currentIndex = -1; } @Override public boolean hasNext() { return currentIndex + 1 < musicList.size(); } @Override public Music next() { if(hasNext()) { currentIndex++; return musicList.get(currentIndex); } return null; } @Override public boolean hasPrevious() { return currentIndex - 1 >= 0; } @Override public Music previous() { if(hasPrevious()) { currentIndex--; return musicList.get(currentIndex); } return null; } } ``` 以上就是一个完整的Java音乐播放器的代码实现,其中用到了工厂方法模式、单例模式、命令模式、装饰器模式、状态模式、外观模式、策略模式以及迭代器模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值