Doug Lea在J.U.C包里面写的BUG又被网友发现了。

Stella981
• 阅读 525

这是why的第 69 篇原创文章

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

BUG描述

一个编号为 8073704 的 JDK BUG,将串联起我的这篇文章。

也就是下面的这个链接。

https://bugs.openjdk.java.net/browse/JDK-8073704

这个 BUG 在 JDK 9 版本中进行了修复。也就是说,如果你用的 JDK 8,也许会遇到这样的问题。

先带大家看看这个问题是怎么样的:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

这个 BUG 说:FutureTask.isDone 方法在任务还没有完成的时候就会返回 true。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

可以看到,这是一个 P4 级别(优先级不高)的 BUG,这个 BUG 也是分配给了 Doug Lea,因为 FutureTask 类就是他写的:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

响应了国家政策:谁污染,谁治理。

这个 BUG 的作者 Martin 老哥是这样描述的:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

下面我会给大家翻译一下他要表达的东西。

但是在翻译之前,我得先做好背景铺垫,以免有的朋友看了后一脸懵逼。

如果要懂他在说什么,那我必须得再给你看个图片,这是 FutureTask 的文档描述:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

看 Martin 老哥提交的 BUG 描述的时候,得对照着状态图和状态对应的数字来看。

他说 FutureTask#isDone 方法现在是这样写的:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

他觉得从源码来看,是只要当前状态不等于 NEW(即不等于0)则返回 true,表示任务完成。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

他觉得应该是这样写:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

这样写的目的是除了判断了 NEW 状态之外,还判断了两个中间状态:COMPLETING 和 INTERRUPTING。

那么除去上面的三个状态之外呢,就只剩下了这四个状态:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

这四个状态可以代表一个任务的最终状态。

当然,他说上面的代码还有优化空间,比如下面这样,代码少了,但是理解起来也得多转个弯:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

state>COMPLETING,满足这个条件的状态只有下面这几种:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

而这几种中,只有 INTERRUPTING 是一个中间态,所以他用后面的 != 排除掉了。

这样就是代码简洁了,但是理解起来多转个小弯。但是这两段代码表示的含义是一模一样的。

好了,关于这个 BUG 的描述就是这样的。

汇总为一句话就是,这个 Martin 老哥认为:

FutureTask.isDone 方法在任务还没有完成的时候,比如还是 COMPLETING 和 INTERRUPTING 的时候就会返回 true,这样是不对的。这就是 BUG。

仅从 isDone 源码中那段 status != NEW 的代码,我认为这个 Martin 老哥说的确实没有问题。因为确实有两个中间态,这段源码中是没有考虑的。

接下来,我们就围绕着这个问题进行展开,看看各位大神的讨论。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

展开讨论

首先,第一个发言的哥们是 Pardeep,是在这个问题被提出的 13 天之后:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

我没有太 get 到这个哥们回答的点是什么啊。

他说:我们应该去看一下 isDone 方法的描述。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

描述上说:如果一个任务已完成,调用这个方法则返回true。而完成除了是正常完成外,还有可能是任务异常或者任务取消导致的完成,这些都算完成。

我觉得他的这个回答和问题有点对不上号,感觉是答非所问。

就当他抛出了一个关于 isDone 方法的知识点吧。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

三天后,第二个发言的哥们叫做 Paul,他的观点是这样的:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

首先,他说我们不需要检查 INTERRUPING 这个中间状态。

因为如果一个任务处于这个状态,那么获取结果的时候一定是抛出 CancellationException。

叫我们看看 isCancelled 方法和 get 方法。

那我们先看看 isCancelled 方法:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

直接判断了状态是否大于等于 CANCELLED,也就是判断了状态是否是这三种中的一个:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

判断任务是否取消(isCancelled)的时候,并没有对 INTERRUPING 这个中间状态做特殊处理。

按照这个逻辑,那么判断任务是否完成(isDone)的时候,也不需要对 INTERRUPING 这个中间状态做特殊处理。

接着,我们看看 get 方法。

get 方法最终会调用这个 report 方法:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

如果变量 s (即状态)是 INTERRUPING (值是 5),那么是大于 CANCELLED (值是 4)状态的,则抛出 CancellationException (CE)异常。

所以,他觉得对于 INTERRUPING 状态没有必要进行检测。

因为如果此状态下,你调用 isCancelled 方法,那么会告诉你任务取消了。

如果你直接调用 get 方法,会抛出 CE 异常。

所以,综上所述,我认为 Paul 这个哥们的逻辑是这样的:

我们作为使用者,最终都会调用 get 方法来获取结果,假设在调用 get 方法之前。我们用 isCancelled 或者 isDone 判断了一下任务的状态。

如果当前状态好死不死的就是 INTERRUPING 。那么调用 isCancelled 返回 true,那按照正常逻辑,是不会继续调用 get 方法的。

如果调用的是 isDone ,那么也返回 true,就会去调用 get 方法。

在 get 方法这里保证了,就算当前处于 INTERRUPING 中间态,程序抛出 CE 异常就可以了。

因此,Paul 认为如果没有必要检测 INTERRUPING 状态的话,那么我们就可以把代码从:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

简化为:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

但是,这个哥们还说了一句话来兜底。

他说:Unless i have missed something subtle about the interactions

除非我没有注意到一些非常小的细节问题。

你看,说话的艺术。话都被他一个人说完了。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

好了,Paul 同学发言完毕了。42 分钟之后,一个叫 Chris 的小老弟接过了话筒,他这样说的:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

我觉得吧,Paul 说的挺有道理的,我赞成他的建议。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

但是吧,我也觉得我们在讨论的是一个非常细节,非常小的问题,我不知道,就算现在这样写,会导致任何问题吗?

写到这里,先给大家捋一下:

  • Martin 老哥提出 BUG 说 FutureTask#isDone 方法没有判断 INTERRUPING 和 COMPLETING 这个两个中间状态是不对的。

  • Paul 同学说,对于 INTERRUPING 这个状态,可以参照 isCancelled 方法,不需要做特殊判断。

  • Chris 小老弟说 Paul 同学说的对。

于是他们觉得 isDone 方法应该修改成这样:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

所以,现在只剩下一个中间状态是有争议的了:COMPLETING 。

对于剩下的这个中间状态,一位叫做 David 的靓仔,在三小时后发表了自己的意见:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

他上来就是一个暴击,直截了当的说:我认为在座的各位都是垃圾。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

好吧,他没有这样说。

其实他说的是:我认为没有必要做任何改变。

COMPLETING 状态是一个转瞬即逝的过渡状态,它代表我们已经有最终状态了,但是在设置最终状态开始和结束的时间间隙内有一个瞬间状态,它就是 COMPLETING 状态。

其实你是可以通过 get 方法知道任务是否是完成了,通过 get 方法你可以获得最终的正确答案。

因为 COMPLETING 这个转瞬即逝的过渡状态是不会被程序给检测到的。

David 靓仔的回答在两个半小时候得到了大佬的肯定:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

Doug Lea 说:现在源码里面是故意这样写的,原因就是 David 这位靓仔说的,我写的时候就是这样考虑过的。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

另外,我觉得这个 BUG 的提交者自己应该解释我们为什么需要修改这部分代码。

其实 Doug 的言外之意就是:你说这部分有问题,你给我举个例子,别只是整理论的,你弄点代码给我看看。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

半小时之后,这个 BUG 的提交者回复了:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

intentional 知道是啥意思不?

害,我又得兼职教英语了:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

他说:哦,原来是故意的呀。

这句话,你用不同的语气可以读出不同的含义。

我这里倾向于他觉得既然 Doug 当初写这段代码的时候考虑到了这点,他分析之后觉得自己这样写是没有问题的,就这样写了。

好嘛,前面说 INTERRUPING 不需要特殊处理,现在说 COMPLETING 状态是检测不到的。

那就没得玩了。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

事情现在看起来已经是被定性了,那就是不需要进行修改。

但是就在这时 Paul 同学杀了个回马枪,应该也是前面的讨论激发了他的思路,你不是说检测不出来吗,你不是说 get 方法可以获得最终的正确结果吗?

那你看看我这段代码是什么情况:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

代码是这样的,大家可以直接粘贴出来,在 JDK 8/9 环境下分别运行一下:

public static void main(String[] args) throws Exception {        AtomicReference<FutureTask<Integer>> a = new AtomicReference<>();        Runnable task = () -> {            while (true) {                FutureTask<Integer> f = new FutureTask<>(() -> 1);                a.set(f);                f.run();            }        };        Supplier<Runnable> observe = () -> () -> {            while (a.get() == null);            int c = 0;            int ic = 0;            while (true) {                c++;                FutureTask<Integer> f = a.get();                while (!f.isDone()) {}                try {                    /*                    Set the interrupt flag of this thread.                    The future reports it is done but in some cases a call to                    "get" will result in an underlying call to "awaitDone" if                    the state is observed to be completing.                    "awaitDone" checks if the thread is interrupted and if so                    throws an InterruptedException.                     */                    Thread.currentThread().interrupt();                    f.get();                }                catch (ExecutionException e) {                    throw new RuntimeException(e);                }                catch (InterruptedException e) {                    ic ++;                    System.out.println("InterruptedException observed when isDone() == true " + c + " " + ic + " " + Thread.currentThread());                }            }        };        CompletableFuture.runAsync(task);        Stream.generate(observe::get)                .limit(Runtime.getRuntime().availableProcessors() - 1)                .forEach(CompletableFuture::runAsync);        Thread.sleep(1000);        System.exit(0);    }

先看一下这段代码的核心逻辑:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

首先标号为 ① 的地方是两个计数器,c 代表的是第一个 while 循环的次数,ic 代表的是抛出 InterruptedException(IE) 的次数。

标号为 ② 的地方是判断当前任务是否是完成状态,如果是,则继续往下。

标号为 ③ 的地方是先中断当前线程,然后调用 get 方法获取任务结果。

标号为 ④ 的地方是如果 get 方法抛出了 IE 异常,则在这里进行记录,打印日志。

需要注意的是,如果打印日志了,说明了一个问题:

前面明明 isDone 方法返回 true 了,说明方法执行完成了。但是我调用 get 方法的时候却抛出了 IE 异常?

这你怕是有点说不通吧!

JDK 8 的运行结果我给大家截个图。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

这个异常是在哪里被抛出来的呢?

awaitDone 方法的入口处,就先检查了当前线程是否被中断,如果被中断了,那么抛出 IE 异常:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

而代码怎么样才能执行到 awaitDone 方法呢?

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

任务状态是小于等于 COMPLETING 的时候。

在示例代码中,前面的 while 循环中的 isDone 方法已经返回了 true,说明当前状态肯定不是 NEW。

那么只剩下个什么东西了?

就只有一个 COMPLETING 状态了。

小样,这不就是监测到了吗?

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

在这段示例代码出来后的第 8 个小时,David 靓仔又来说话了:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

他要表达的意思,我理解的是这样的:

在 j.u.c 包里面,优先检查线程中断状态是很常见的操作,因为相对来说,会导致线程中断的地方非常的少。

但是不能因为少,我们就不检查了。

我们还是得对其进行了一个优先检查,告知程序当前线程是否发生了中断,即是否有继续往下执行的意义。

但是,在这个场景中,当前线程中断了,但并不能表示 Future 里面的 task 任务的完成情况。这是两个不相关的事情。

即使当前线程中断了,但是 task 任务仍然可以继续完成。但是执行 get 方法的线程被中断了,所以可能会抛出 InterruptedException。

因为,他给出的解决建议是:

可以选择优先返回结果,在 awaitDone 方法的循环中把检查中断的代码挪到后面去。

五天之后,之前 BUG 的提交者 Martin 同学又来了:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

他说他改变主意了。

改变什么主意了?他之前的主意是什么?

在 Doug 说他是故意这样写的之后,Martin 说:

It's intentional。哦,原来是故意的呀。

那个时候他的主意就是:大佬都说了,这样写是考虑过的,肯定没有问题。

现在他的主意是:如果 isDone 方法返回了 true,那么 get 方法应该明确的返回结果值,而不会抛出 IE 异常。

需要注意的是,这个时候对于 BUG 的描述已经发生变化了。

从“FutureTask.isDone 方法在任务还没有完成的时候就会返回 true”变成了“如果 isDone 方法返回了 true,那么 get 方法应该明确的返回结果值,而不会抛出 IE 异常”。

然后 David 靓仔给出了一个最简单的解决方案:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

最简单的解决方案就是先检查状态,再检查当前线程是否中断。

然后,这个 BUG 由 Martin 同学进行了修复:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

修复的代码可以先不看,下面一小节我会给大家做个对比。

他修复的同时还小心翼翼的要求 Doug 祝福他,为他站个台。

最后,Martin 同学说他已经提交给了 jsr166,预计在 JDK 9 版本进行修复。

出于好奇,我在 JDK 的源码中搜索了一下 Martin 同学的名字,本以为是个青铜,没想到是个王者,失敬失敬:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

代码对比

既然说在 JDK 9 中对该 BUG 进行了修复,那么带大家对比一下 JDK 9/8 的代码。

java.util.concurrent.FutureTask#awaitDone:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

可以看到,JDK 9 把检查是否中断的操作延后了一步。

代码修改为这样后,把之前的那段示例代码放到 JDK 9 上面跑一下,你会惊奇的发现,没有抛出异常了。

因为源码里面判断 COMPLETING 的操作在判断线程中断标识之前:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

我想就不需要我再过多解释了吧。

然后多说一句 JDK 9 现在的 FutureTask#awaitDone 里面有这样的一行注释:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

它说:isDone 方法已经告诉使用者任务已经完成了,那么调用 get 方法的时候我们就不应该什么都不返回或者抛出一个 IE 异常。

这行注释想要表达的东西,就是上面一小节的 BUG 里面我们在讨论的事情。写这行注释的人,就是 Martin 同学。

当我了解了这个 BUG 的来龙去脉之后,又突然间在 JDK 9 的源码里面看到这个注释的时候,有一种很神奇的感觉。

就是一种源码说:you feel me?

我马上心领神会:I get you。

挺好。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

虚假唤醒

在 JDK 9 的注释里面还有这个词汇:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

spurious wakeup,虚假唤醒。

如果你之前不知道这个东西的存在,那么恭喜你,又 get 到了一个你基本上用不到的知识点。

除非你自己需要在代码中用到 wait、notify 这样的方法。

哦,也不对,面试的时候可能会用到。

“虚假唤醒”是怎么一回事呢,我给你看个例子:

java.lang.Thread#join(long) 方法:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

这里为什么要用 while 循环,而不是直接用 if 呢?

因为循环体内有调用 wait 方法。

为什么调用了 wait 方法就必须用 while 循环呢?

别问,问就是防止虚假唤醒。

看一下 wait 方法的 javadoc:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

一个线程能在没有被通知、中断或超时的情况下唤醒,也即所谓的“虚假唤醒”,虽然这点在实践中很少发生,但是程序应该循环检测导致线程唤醒的条件,并在条件不满足的情况下继续等待,来防止虚假唤醒。

所以,建议写法是这样的:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

在 join 方法中,isAlive 方法就是这里的 condition does not hold。

在《Effective Java》一书中也有提到“虚假唤醒”的地方:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

书中的建议是:没有理由在新开发的代码中使用 wait、notify 方法,即使有,也应该是极少了,请多使用并发工具类。

再送你一个面试题:为什么 wait 方法必须放在 while 循环体内执行?

现在你能回答的上来这个问题了吧。

关于“虚假唤醒”就说这么多,有兴趣的同学可以再去仔细了解一下。

Netty的一个BUG

好好的说着 JDK 的 FutureTask 呢,怎么突然转弯到 Netty 上了?

因为 Netty 里面,其核心的 Future 接口实现中,犯了一个基本的逻辑错误,在实现 cancel 和 isDone 方法时违反了 JDK 的约定。

这是一个让 Netty 作者也感到惊讶的错误。

先看看 JDK Future 接口中,对于 cancel 方法的说明:

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

文档的方法说明上说:如果调用了 cancel 方法,那么再调用 isDone 将永远返回 true。

看一下这个测试代码:

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

可以看到,在调用了 cancel 方法后,再次调用 isDone 方法,返回的却是 false。

这个点我是很久之前在知乎的这篇文章上看到的,和本文讨论的内容有一点点相关度,我就又翻了出来,多说了一嘴。

有兴趣的可以看看:

《一个让Netty作者也感到惊讶的错误》

https://zhuanlan.zhihu.com/p/34609401

荒腔走板

真人演说版的荒腔走板,好男人石锤视频,去 B 站看吧,随便关注关注我,我谢谢您呢。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

最后说一句(求关注)

好了,看到了这里安排个“一键三连”(转发、在看、点赞)吧,周更很累的,不要白嫖我,需要一点正反馈。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

才疏学浅,难免会有纰漏,如果你发现了错误的地方,可以在留言区提出来,我对其加以修改。

感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

我是 why,一个被代码耽误的文学创作者,不是大佬,但是喜欢分享,是一个又暖又有料的四川好男人。(不信你去 B 站看我视频)

还有, 欢迎关注我呀!

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

往期回顾

[

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

关于多线程中抛异常的这个面试题我再说最后一次!

](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIxNTQ4MzE1NA%3D%3D%26mid%3D2247495327%26idx%3D1%26sn%3Dc38b049a6225dfe95cac5dbb7c46e92b%26chksm%3D979538e2a0e2b1f4e2f29dea6aedf2938254816aa9e7a1c0798ae751b7e2b3a3046692c34e96%26scene%3D21%23wechat_redirect)

[

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

why哥被阿里一道基础面试题给干懵了,一气之下写出万字长文。

](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIxNTQ4MzE1NA%3D%3D%26mid%3D2247494882%26idx%3D1%26sn%3D2e918c77f1dbfa6dc3d8bd58f036bd4c%26chksm%3D97953a9fa0e2b389c93d01a7afb9f07c88f84d31fa0ade12062cc9dd1622918e708b46ef088e%26scene%3D21%23wechat_redirect)

[

Doug Lea在J.U.C包里面写的BUG又被网友发现了。

why哥这里有一道Dubbo高频面试题,请查收。

](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIxNTQ4MzE1NA%3D%3D%26mid%3D2247494089%26idx%3D1%26sn%3D76200bbdb6ac1169ab7876e6d6a96ad8%26chksm%3D97953fb4a0e2b6a2e4c4f4f75e62cd641fedb0cae371731df72c7f0547420b986d4301415727%26scene%3D21%23wechat_redirect)

留言区

本文分享自微信公众号 - why技术(hello_hi_why)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这