
ConcurrentLinkedQueue 的 offer 方法有个奇怪的三目表达式
*/ public boolean offer(E e) { checkNotNull(e); final Node<E> newNode = new Node<E>(e); for (Node<E> t = tail, p = t;;){ Node<E> q = p.next; if (q == null) { // p is last node if (p.casNext(null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this queue, // and for newNode to become "live". if (p != t) // hop two nodes at a time casTail(t, newNode); // Failure is OK. return true; } // Lost CAS race to another thread; re-read next } else if (p == q) // We have fallen off list. If tail is unchanged, it // will also be off-list, in which case we need to // jump to head, from which all live nodes are always // reachable. Else the new tail is a better bet. p = (t != (t = tail)) ? t : head; else // Check for tail updates after two hops. p = (p != t && t != (t = tail)) ? t : q; } } 这个三目表达式(t != (t = tail)) ? t : head;的左边看起来很奇怪,(t != (t = tail))本质上不就是(t != t)吗,那这不是肯定不成立吗?我佛了
jdk8
1 TtTtTtT 2020-07-20 12:18:04 +08:00 =。=本质理解错了呀。。 x = (t != (t = tail)) 是 newT = tail x = t != newT t = newT |
2 amiwrong123 OP |
3 zmj1316 2020-07-20 12:44:33 +08:00 via Android 取值有顺序的,左边的 t 早于右边的赋值吧 |
4 no1xsyzy 2020-07-20 13:00:42 +08:00 话说这也不是三目看不懂啊…… |
5 chairuosen 2020-07-20 13:11:21 +08:00 谁这么写代码我打死他,相当于 get 里干 set 的活 |
6 TtTtTtT> 2020-07-20 13:47:38 +08:00 @amiwrong123 你说的我完全没弄懂。。 我给你简单解释一下: t = tail 是用来获取当前最新的 tail 。 (t != (t = tail)) 就是检查一下 t 是不是最新的 tail,同时将 t 更新为最新的 tail 。 在 Java 中,赋值语句返回所赋的值,比如 a = 1 返回 a 。 @chairuosen JDK 里这种写法很多,这样编译的时候会减少 slots 的占用。 |
7 TtTtTtT 2020-07-20 13:48:18 +08:00 @amiwrong123 写错了,a = 1 返回 1 。 |
8 hangs 2020-07-20 13:56:29 +08:00 这里有个说明,我觉得说的很明白了 https://segmentfault.com/q/1010000022097461/a-1020000022098182 我理解一个是赋值表达式本身是有返回的,返回值正如 @TtTtTtT 所说,是被赋值的值,另一个是开头的 t,其实读的还是旧值 |
9 chairuosen 2020-07-20 14:01:38 +08:00 @TtTtTtT #6 写代码第一给人看,第二才是给机器看。如果是底层万年不变的可以用写奇技淫巧,业务代码这么写被喷成狗 |
10 amiwrong123 OP @TtTtTtT 赋值语句我懂了,而且这个比较运算之前我好像理解错了。 (t != (t = tail)) 实际上是左边的 t 先读取旧 t,然后执行到等号右边,执行右边的赋值语句,虽然赋值语句改变了 t 的值,但由于执行顺序,左边的 t 已经“感受”不到变化了。 |
11 bzj 2020-07-20 15:36:42 +08:00 |
12 mightofcode 2020-07-20 16:53:41 +08:00 这代码味道有点冲 |
![]() | 13 SingeeKing PRO |
14 xiangyuecn 2020-07-20 17:04:43 +08:00 骚操作,并没有省代码,反而理解起来异常困难。 p = (t != (t = tail)) ? t : head; 等价于: p= t != tail ? tail:head; t=tail; 多写一行多好理解,为了省一行写成这样也行啊: p= t != tail ? tail:head; t=tail; 狗头 |
15 amiwrong123 OP @bzj 我也想知道,这可能就是大佬的写法吧。。 |
16 amiwrong123 OP @SingeeKing 昨天正准备开开心心看看 ConcurrentLinkedQueue 的源码,结果就被这个三木表达式给困住了 |
17 amiwrong123 OP |
18 blessingsi 2020-07-20 17:27:36 +08:00 jdk 里面这种代码太多了,上次看 concurrentmap,发现给 local 变量加了锁,翻了无数遍,才发现 local 变量后面又赋值了成员变量 |
19 amiwrong123 OP @blessingsi 哈哈,有点好奇是哪个地方。话说你是说 ConcurrentHashMap 嘛,我也刚看了 ConcurrentHashMap,还是说你看的是那个 concurrentmap 接口 |
20 yyyyfan 2020-07-20 18:10:33 +08:00 via Android 你以为的源码是大佬在写,然而其实是工人在拧螺丝 |
21 szzhiyang 2020-07-20 18:13:50 +08:00 Go 代码就不会有这样晦涩难懂的表达。 |
22 amiwrong123 OP @yyyyfan 原来是这样的吗。可是看作者就是 Doug Lea 啊 |
23 amiwrong123 OP @szzhiyang 原来这就是 go 吗,爱了爱了 |
24 TtTtTtT 2020-07-20 20:24:46 +08:00 @chairuosen 正因为这不是业务代码,所以才要剩下一个 slots. |
25 TtTtTtT 2020-07-20 20:27:40 +08:00 @xiangyuecn 不对,因为 tail 是个 mutable 的,所以每次拿的时候就要存下来,否则就会存在变化的可能。 你可以用一个新的 ref 去引用你新拿到的 tail,但是如前所说会多一个 slot 。 p = (t != (t = tail)) ? t : head; 等价于: val tmpT = tail; p = t != tmpT ? tmpT : head; t = tmpT; |
27 Jooooooooo 2020-07-20 20:29:35 +08:00 质疑别人无所谓, 但是质疑 Doug Lea 需要更多的证据. |
28 tikazyq 2020-07-20 21:20:32 +08:00 这种代码是确定没有人质疑它的可读性? |
29 misaka19000 2020-07-20 21:31:22 +08:00 @xiangyuecn #14 多写一行不觉得很丑吗? |
30 misaka19000 2020-07-20 21:33:13 +08:00 这种代码和业务代码不一样,这种代码一旦写好基本上不存在修改的可能,所以可以降低可读性来提高代码的简洁性。 |
31 786375312123 2020-07-20 21:34:11 +08:00 代码写的不好,和三目运算符没关系 |
32 786375312123 2020-07-20 21:34:41 +08:00 @misaka19000 代码简洁的目的是什么? |
33 misaka19000 2020-07-20 21:37:32 +08:00 @786375312123 #31 好看啊,能写一行的为啥要花两行来写 |
34 786375312123 2020-07-20 21:38:52 +08:00 @misaka19000 好看有啥用? |
35 misaka19000 2020-07-20 21:48:10 +08:00 @786375312123 #33 你要这么说那我无话可说,人各有志 |
36 786375312123 2020-07-20 21:49:36 +08:00 @misaka19000 不是,我就是好奇,这种“一行比两行好的好看”,有啥用? |
37 talen666 2020-07-20 22:05:51 +08:00 这不是 Lock 锁的核心代码吗~当时看的时候,也是迷糊了一会。按照括号来,后来看懂了 |
38 fakeshadow 2020-07-20 22:19:01 +08:00 没写过 java,但是 cas 操作这么写很可能是有原因的。memory order 和 race 问题你用一般业务的写法有时反而更难懂 |
39 apporoad 2020-07-21 04:47:38 +08:00 代码自带混淆 |
41 vansouth 2020-07-21 10:19:00 +08:00 虽然我看得懂 但是我不喜欢这种写法 |
42 amiwrong123 OP @talen666 哪段代码,让我也康康,话说你说的是 reentrantlock 么 |
43 amiwrong123 OP @fakeshadow 额,可是三目表达式这里没涉及到 cas 啊,只有 valitale 读 |
44 amiwrong123 OP @apporoad 哈哈哈哈,老哥你挺逗 |
45 amiwrong123 OP @vansouth 本帖的主题可能要变成 简洁性 和 可读性 的争辩了。 |
46 yamasa 2020-07-21 10:57:39 +08:00 我觉得那些个 conc 包的类作者就没怎么考虑过可读性。不过 comment 倒是挺足的 |
47 zhangpeter 2020-07-21 10:59:14 +08:00 t != (t = tail) 等价于: if (t != tail) t = tail; |
48 amiwrong123 OP @yamasa 嗯,好像是。尤其是无锁编程 lock free 实现的那几个类(比如那个 concurrent 跳表),不看注释根本看不懂啊 |
49 whitehack 2020-07-21 13:40:52 +08:00 ``` > let t = 2 undefined > let tail = 3 undefined > t!=(t=tail) true > t 3 > ``` |
50 2kCS5c0b0ITXE5k2 2020-07-21 14:19:55 +08:00 @misaka19000 ``` reduce(lambda x, y: x + y, map(lambda i: l[i] + 3, list(filter(lambda y: y % 2 == 0, range(len(l)))))) ``` |
51 Boyce 2020-07-21 14:59:29 +08:00 我服了,不看评论看不懂系列。 |
52 lovelive1024 2020-07-21 15:18:46 +08:00 如果只一行是为了好看 p = (t != (t = tail)) ? t : hea 这种写法不是更好看 p = t != tail ? t = tail : head; 而这种可读性不是更强 p = (t != tail) ? (t = tail) : head; |
53 atwoodSoInterest 2020-07-21 15:41:51 +08:00 |
54 palmers 2020-07-21 16:16:48 +08:00 我做了一个测试, 估计大概就明白了. ```java Object t = new Object(); Object h = new Object(); System.out.println("t: " + t); System.out.println("h: " + h); System.out.println("(t = h): " + (t = h)); ``` |
55 forestn 2020-07-21 16:21:35 +08:00 via iPhone 不知道对不对 这是非阻塞算法 cas 吧 |
56 palmers 2020-07-22 09:17:31 +08:00 @palmers 我简单模拟了一下 然后反编译后字节码是这样的 源码: ```java public void test() { Object t = new Object(); Object h = new Object(); Object c = t != (t = h) ? t : h; } ``` 反编译后字节码: ```code public void test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=4, args_size=1 0: new #5 // class java/lang/Object 3: dup 4: invokespecial #1 // Method java/lang/Object."<init>":()V 7: astore_1 8: new #5 // class java/lang/Object 11: dup 12: invokespecial #1 // Method java/lang/Object."<init>":()V 15: astore_2 16: aload_1 17: aload_2 18: dup 19: astore_1 20: if_acmpeq 27 23: aload_1 24: goto 28 27: aload_2 28: astore_3 29: return LineNumberTable: line 15: 0 line 16: 8 line 17: 16 line 18: 29 LocalVariableTable: Start Length Slot Name Signature 0 30 0 this Lcom/jd/pricewarning/plussdk/worker/web/Ti; 8 22 1 t Ljava/lang/Object; 16 14 2 h Ljava/lang/Object; 29 1 3 c Ljava/lang/Object; StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 27 locals = [ class java/lang/Object, class java/lang/Object ] frame_type = 64 /* same_locals_1_stack_item */ stack = [ class java/lang/Object ] ``` |
57 fangcan 2020-07-22 15:44:33 +08:00 这是 cas 的体现 |
58 xiangbohua 2020-08-21 17:00:28 +08:00 “代码首先写给人看的,其次才是写给机器的” 这句话我十分的不同意,代码写出来不是让机器跑的,是让人天天朗读的,你当是编课本呢? 这句话之所以出名,是因为现在的憨憨程序员太多了,为了让后进来的倒霉蛋好接手一点,才制定的要求,提高所谓可读性(当然我自己也憨,也接手过别人的代码)。 然鹅,真正的大佬,都有种“老子就爱这么写,看不懂就别看,我的代码不需要维护!”的气概。。。(脑海浮现出 Linus Torvalds 指着你鼻子.jpg ) 我是这么认为的 |