厉害了,美女同事用单例模式实现了雪花算法!

用15个字符表示时间串,比如2021年06月30日14点52分30秒226毫秒可以表示为210630145230226。这么表示可读性更强,而且百年之内不会重复

用两位数字表示机器编号,最多可以支持100个机器

用两位数字表示序列号,一毫秒内支持100个并发

接下来把我们改编后的算法用代码实现一下(这里贴的是图片,文末会附上源码)

这就实现了我们改编过后的雪花算法。但是,仔细想一下,代码还存在并发问题

在两个线程同时执行这块代码时获取的唯一编号有可能重复

这是因为线程A执行到某一行时被挂起,还没来得及修改lastTime的值。比如线程A执行到这一行时被挂起

这时线程B开始执行,判断lastTimenowTime还是equals的,线程B就会继续执行并且获得一个编号

然后线程A被唤起继续执行也获取到一个编号,这时两个线程获取到的编号就重复了

可以用java的synchronized关键字把并发改为同步

加上synchronized后,当线程A在方法中正执行时,线程B只能在方法外等待,不能进去执行,这就解决了上面说的并发问题

但是,还有另外一个问题。

我们都知道synchronized只针对同一个实例有效,当有多个实例时,多个实例之间无法控制

一旦产生多个实例时,多个实例之间产生的编号就有可能重复

所以我们不能让这个类的对象产生多个实例,只能让它始终保持只有一个实例

说到这里,我们首先想到的就是单例模式。另外,设计模式系列面试题和答案全部整理好了,微信搜索Java技术栈,在后台发送:面试,可以在线阅读。

单例模式最大的特点就是在任何情况下最多只有一个实例,所以这里使用单例模式来解决这个问题再合适不过

先说一下单例模式怎么保证单例,要想保证单例就不能让别人随便创建实例。

最好的办法就是把构造器私有化,让它是private的。私有化之后只有这个类自己能创建实例,其它的类都没有调用这个类的构造器的权限

这个类只创建一个实例,那么它就是单例的

单例模式的创建可分为懒汉式创建和饿汉式创建

懒汉式单例模式

=======

懒汉式从字面意思理解就是懒嘛,因为我懒,能歇着就不会动,你没让我干活我就不会主动去干

所以,懒汉式单例模式的实例一开始为空,等到被调用时才会初始化

懒汉式单例模式有多种实现方式,首先我们先来看第一种

加上红框中的内容就变成了懒汉式单例模式

但是这个单例模式在并发情况下是有可能会产生多个实例的

两个线程获取的实例的内存地址是不一样的,说明获取到的是多个实例

这是因为在并发情况下线程A执行到某一行时被挂起,还没来得及创建实例。比如下面这一行

这时线程B开始执行,到18行时判断还没创建实例,线程B就创建了一个实例

然后线程A被唤起,接着往下执行,也会创建一个实例

这个问题和我们刚才讲雪花算法的时候遇到的问题一样,可以用synchronized来解决

加上synchronized以后,当一个线程在执行被synchronized锁住的代码时,其他线程只能等待。

当这个线程执行完之后,创建了snowFlake实例。然后别的线程才能进去执行

当别的线程进去执行的时候,发现snowFlake不是null了,就不会创建新的实例了

这就解决了懒汉式单例模式在并发情况下创建多个实例的问题,但是还不够完美

试想一下,当并发量很大的时候,因为只有一个线程可以进去执行,其他线程只能在外面等待。

随着访问量越来越大,被阻塞的线程也越来越多。当阻塞的线程足够多时,就有可能导致服务器宕机

我们可以这样优化,在synchronized外面再加一层非空判断

加上外层的非空判断之后,虽然synchronized还是会阻塞后面过来的线程

但是,当第一个线程执行完之后,snowFlake被实例化,不再为null

因为有外层的非空判断,所以后续的线程不会再进去执行,也不会被阻塞,而是直接return了

这就是一个完美的懒汉式单例模式了

饿汉式单例模式

=======

饿汉式从字面意思理解就是饿嘛,因为我一直饿,所以把好吃的都提前给我准备好

所以饿汉式单例模式的实例是提前创建好的,也就是类加载的时候就创建了,而不是等到用的时候再创建

我们用饿汉式单例模式来优化一下我们之前改编的雪花算法

加上红框中的代码雪花算法就变成了饿汉式单例模式。

红框中第一行的snowFlake变量是被static修饰的,我们都知道static修饰的变量是属于这个类的,在类加载的时候就进行了初始化赋值。

而这个类只会被加载一次,所以snowFlake变量只会被初始化一次,从而保证了单例。

推荐:单例模式的 8 种写法,非常全!

源码

==

下面附上饿汉式和懒汉式创建雪花算法单例模式的源码,需要的请自取

**「饿汉式」**单例模式实现雪花算法

package com.helianxiaowu.hungrySingleton;

import java.time.LocalDateTime;

import java.time.format.DateTimeFormatter;

/**

* @desc: 饿汉式单例模式实现雪花算法

* @author: 公众号:赫连小伍

* @create: 2021-06-29 19:32

**/

public class SnowFlake {

private static SnowFlake snowFlake = new SnowFlake();

private SnowFlake() {}

public static SnowFlake getInstance() {

return snowFlake;

}

// 序列号,同一毫秒内用此参数来控制并发

private long sequence = 0L;

// 上一次生成编号的时间串,格式:yyMMddHHmmssSSS

private String lastTime = “”;

public synchronized String getNum() {

String nowTime = getTime(); // 获取当前时间串,格式:yyMMddHHmmssSSS

String machineId = “01”; // 机器编号,这里假装获取到的机器编号是2。实际项目中可从配置文件中读取

// 本次和上次不是同一毫秒,直接生成编号返回

if (!lastTime.equals(nowTime)) {

sequence = 0L; // 重置序列号,方便下次使用

lastTime = nowTime; // 更新时间串,方便下次使用

return new StringBuilder(nowTime).append(machineId).append(sequence).toString();

}

// 本次和上次在同一个毫秒内,需要用序列号控制并发

if (sequence < 99) { // 序列号没有达到最大值,直接生成编号返回

sequence = sequence + 1;

return new StringBuilder(nowTime).append(machineId).append(sequence).toString();

}

// 序列号达到最大值,需要等待下一毫秒的到来

while (lastTime.equals(nowTime)) {

nowTime = getTime();

}

sequence = 0L; // 重置序列号,方便下次使用

lastTime = nowTime; // 更新时间串,方便下次使用

return new StringBuilder(nowTime).append(machineId).append(sequence).toString();

}

private String getTime() {

return LocalDateTime.now().format(DateTimeFormatter.ofPattern(“yyMMddHHmmssSSS”));

}

}

**「懒汉式」**单例模式实现雪花算法

package com.helianxiaowu.lazySingleton;

import java.time.LocalDateTime;

import java.time.format.DateTimeFormatter;

/**

* @desc: 懒汉式单例模式实现雪花算法

* @author: 公众号:赫连小伍

* @create: 2021-06-29 19:32

**/

public class SnowFlake {

private static SnowFlake snowFlake = null;

private SnowFlake() {}

public static SnowFlake getInstance() {

if (snowFlake == null) {

synchronized (SnowFlake.class) {

if (snowFlake == null) {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

给大家送一个小福利

附高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、准备面试跳槽、自身职业规划迷茫的朋友们。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)**

img

最后

给大家送一个小福利

[外链图片转存中…(img-ITmc38J4-1713556986088)]

附高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、准备面试跳槽、自身职业规划迷茫的朋友们。

[外链图片转存中…(img-TOavqvJe-1713556986088)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值