Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
N
note
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
黄夏豪
note
Commits
c44be931
提交
c44be931
authored
6月 18, 2020
作者:
xuyang
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
threadQuestions
上级
7ae36af3
隐藏空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
192 行增加
和
38 行删除
+192
-38
ThreadDemo.java
许旸/snowflake/ThreadDemo.java
+72
-0
snowflake.md
许旸/snowflake/snowflake.md
+120
-38
image-20200618104341101.png
许旸/snowflake/upload/image-20200618104341101.png
+0
-0
image-20200618141052118.png
许旸/snowflake/upload/image-20200618141052118.png
+0
-0
没有找到文件。
许旸/snowflake/ThreadDemo.java
0 → 100644
浏览文件 @
c44be931
package
api
.
thread
.
memory_barrier
;
import
java.util.concurrent.atomic.AtomicBoolean
;
/**
* 测试多线程数据不一致问题
*/
class
ThreadDemo
implements
Runnable
{
//Solution1: volatile
/*private volatile boolean flag = false;*/
private
boolean
flag
=
false
;
//flag初始化为false
//Solution6: CAS
/*private AtomicBoolean flag = new AtomicBoolean(false);*/
@Override
public
void
run
()
{
//将flag设置为true
flag
=
true
;
/*flag.getAndSet(true);*/
System
.
out
.
println
(
"flag="
+
flag
);
}
public
boolean
isFlag
()
{
return
flag
;
}
/*public AtomicBoolean isFlag() {
return flag;
}*/
}
/**
* main
*/
class
Main
{
public
static
void
main
(
String
[]
args
)
{
ThreadDemo
threadDemo
=
new
ThreadDemo
();
new
Thread
(
threadDemo
).
start
();
//启动线程threadDemo,线程中设置flag为true
//Solution3: lock
/*Lock lock = new ReentrantLock();
lock.lock();*/
while
(
true
)
{
//Solution4: sleep()
/*try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
//Solution5: sout
/*System.out.println("output here");*/
//Solution2: synchronized
/*synchronized (threadDemo) {*/
if
(
threadDemo
.
isFlag
())
{
//主线程中判断flag为true/false
/*if (threadDemo.isFlag().get()) {*/
System
.
out
.
println
(
"----------------"
);
break
;
}
/*}*/
}
/*lock.unlock();*/
}
}
\ No newline at end of file
许旸/snowflake/snowflake.md
浏览文件 @
c44be931
### **1. 数据库自增长序列或字段**
最常见的方式。利用数据库,全数据库唯一。
最常见的方式。利用数据库,全数据库唯一。
优点:
**· 优点:**
1)简单,代码方便,性能可以接受
。
1)使用INT自增,消耗空间小,性能好
。
2)数字ID天然排序,对分页或者需要排序的结果很有帮助
。
2)数字ID天然排序,便于需要分页和排序的场合
。
缺点:
**· 缺点:**
1)不同数据库语法和实现不同,数据库迁移的时候或多数据库版本支持的时候需要处理。
1)不同数据库语法和实现不同,数据库迁移的时候或多数据库版本支持的时候需要处理。
2)在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成。有单点故障的风险。
2)在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成。有单点故障的风险。
3)在性能达不到要求的情况下,比较难于扩展。
3)在性能达不到要求的情况下,比较难于扩展。
4)如果遇见多个系统需要合并或者涉及到数据迁移会相当痛苦
。
4)如果多个系统需要合并或者涉及到数据迁移,可能会主键重复
。
5)分表分库的时候会有麻烦
。
5)分表分库的时候,也可能造成主键重复
。
优化方案:
**· 优化:**
1)针对主库单点,如果有多个Master库,则每个Master库设置的起始数字不一样,步长一样,可以是Master的个数。比如:Master1 生成的是 1,4,7,10,Master2生成的是2,5,8,11 Master3生成的是
3,6,9,12。这样就可以有效生成集群中的唯一ID,也可以大大降低ID生成数据库操作的负载。
1)针对主库单点,如果有多个Master库,则每个Master库设置的起始数字不一样,步长一样,可以是 Master的个数。比如:Master1 生成的是 1,4,7,10,Master2生成的是2,5,8,11,Master3生成的是
3,6,9,12。这样就可以有效生成集群中的唯一ID,也可以大大降低ID生成数据库操作的负载。
### **2. UUID**
常见的方式。可以利用数据库也可以利用程序生成,一般来说全球唯一
。
常见的方式。可以利用数据库也可以利用程序生成
。
优点:
**· 优点:**
1)简单,代码方便
。
1)可以很简单地使用代码生成
。
2)生成ID性能非常好,基本不会有性能问题。
2)生成ID性能非常好,基本不会有性能问题。
3)全球唯一,在遇见数据迁移,系统数据合并,或者数据库变更等情况下,可以从容应对
。
3)保证在全球范围的唯一性,适用于数据迁移,系统数据合并,或者数据库变更等情况下
。
缺点:
**· 缺点:**
1)没有排序,无法保证趋势递增。
1)没有排序,无法保证趋势递增。
2)UUID往往是使用字符串存储,查询的效率比较低。
2)UUID往往是使用字符串存储,查询的效率比较低。
3)存储空间比较大,如果是海量数据库,就需要考虑存储量的问题。
3)存储空间比较大,如果是海量数据库,就需要考虑存储量的问题。
4)传输数据量大
4)传输数据量大。
5)不可读
。
5)可读性很差
。
### **3.snowflake算法 **
snowflake是Twitter开源的分布式ID生成算法。
snowflake是Twitter开源的分布式ID生成算法。
**· 优点:**
1)不依赖于数据库,灵活方便,且性能优于数据库。
1)不依赖于数据库,灵活方便,且性能优于数据库。
2)ID按照时间在单机上是递增的。
2)ID按照时间在单机上是递增的。
**· 缺点:**
1)在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会
出现不是全局递增的情况。
1)在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会
出现不是全局递增的情况。
2)存在时间回退问题,时间回退后,生成的ID就有可能重复。
2)存在时间回退问题,时间回退后,生成的ID就有可能重复。
**· ID结构:**
结果是一个long型的ID,分成四个部分:
结果是一个long型的ID,分成四个部分:
0(符号位)— 000···000(41位表示毫秒数)— 000···000(10位表示机器的ID)— 000···000(12位 作为序列号)
**· 实现方法(生成一个ID的流程):**
1)设置一个初始对照时间戳
*lastTime*
,默认为0
2)获取当前时间戳
*currentTime*
与
*lastTime*
比较,假如在同一毫秒内,序列号+1;假如
*currentTime*
>
*lastTime*
,重置序列号为0;假如
*currentTime*
<
*lastTime*
,说明时间倒退应该报错。
3)若序列号达到最大值(12位全为1),则会触发等待直到下一毫秒,并将下一毫秒的时间戳作为
*currentTime*
4)将
*lastTime*
更新为
*currentTime*
作为对照时间戳
·
**优化(时间回溯):**
1)可以使用RingBuffer环形数组,预先生成一批ID,放在数组中。当低于阈值时触发数据填充。填充的时 候时间戳为当前的时间戳。
2)可以缩短ID中时间占用的位数,增加序列号或者机器号的位数。
3)单线程情况下QPS较多线程更高。
### **4.关于多线程中出现的一些问题 **
```
//出现的问题
public class ThreadDemo implements Runnable {
private boolean flag = false; //flag初始化为false
@Override
public void run() { //将flag设置为true
flag = true;
System.out.println("flag=" + flag);
}
public boolean isFlag() {
return flag;
}
}
public class Main {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start(); //启动线程threadDemo,线程中设置flag为true
while (true) {
if(threadDemo.isFlag()) { //主线程中判断flag为true/false
System.out.println("----------------");
break;
}
}
}
}
//启动main方法后,只有输出flag=true,但是程序并没有停止。
```
**· 多线程数据共享的流程图:**

· JVM会为每个线程分配独立的工作内存(缓存)。
· 当一个线程线程需要读取数据时,会先去缓存中获取数据,若缓存中没有需要的数据,则会从主存中获取数据再加载到缓存中。
· 当一个线程有对数据进行修改后,会先刷新缓存中的数据,再经过缓存刷新主存中的数据。
· 当主存数据更新时,会及时刷新各个缓存中对应的值。
· 在案例中线程threadDemo修改flag为true,刷新了自身缓存与主存中的flag值,然而main线程在while循环下CPU占用过高,高频读取自身缓存中flag值,导致缓存中的flag值不能及时从主存中刷新。
**· 对于共享数据不一致的解决措施:**
1)共享变量使用volatile关键字,利用其可见性和禁止重排的特性(CPU为了提高效率可能会重排)
2)使用synchronized锁
3)使用lock锁
4)main方法中调用Thread.sleep()方法
5)在while(true)中System.out.print输出,利用其内部Syncronized锁
0(符号位)— 000···000(41位表示毫秒数)— 000···000(10位表示机器的ID)— 000···000(12位作为序列号)
6)CAS保证原子性
**· 原理:**
……
①设置一个初始对照时间戳
*lastTime*
,默认为0
**· 关于volatile的tips**
②获取当前时间戳
*currentTime*
与
*lastTime*
比较,假如在同一毫秒内,序列号+1;假如
*currentTime*
>
*lastTime*
,重置序列号为0;假如
*currentTime*
<
*lastTime*
,说明时间倒退应该报错
。
1)针对含有volatile修饰的参数的指令,禁止该指令与之前和之后的读和写指令重排序
。
③若序列号达到最大值(12位全为1),则会触发等待直到下一毫秒,并将下一毫秒的时间戳作为
*currentTime*
2)把写缓冲区中的所有数据刷新到内存中。进行写入操作时,会在写后面加上一条store指令,将本地内 存中的共享变量值立即刷新到主存。(即内存屏障)
④将
*lastTime*
更新为
*currentTime*
作为对照时间戳
3)使其他处理器里的缓存行无效。在进行读操作时,会在读前面加上一条load指令,从主存中读取变量。
·
**关于如何解决时间回退问题:**
4)无法保证原子性。例如volatile int a = 0; a++; 其中a++不是原子操作,仍可能会发生重排序。
可以使用RingBuffer环形数组,预先生成一批ID,放在数组中。当低于阈值时触发数据填充。填充的时候时间戳为当前的时间戳。
\ No newline at end of file
\ No newline at end of file
许旸/snowflake/upload/image-20200618104341101.png
0 → 100644
浏览文件 @
c44be931
88.5 KB
许旸/snowflake/upload/image-20200618141052118.png
0 → 100644
浏览文件 @
c44be931
71.5 KB
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论