SherlockGy's Blog

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

Java多线程学习笔记

发表于 2020-07-27 | 分类于 程序人生 > Java > Java多线程
本文字数: 2.6k | 阅读时长 ≈ 2 分钟

Java多线程学习笔记

CAS

compare and swap,比较并且交换,同compare and exchange

image-20200726215411185

CAS可以在没有加锁的情况下,保值多线程的一致性。

如何解决ABA问题:

  1. 加版本号
  2. 使用一个bool类型作标记
  3. 没有影响的话,可以不考虑此问题

CAS如何实现的

hotspot源码,cpp内,调用了汇编指令cmpxchg,如果是多核CPU,还需要lock

1
lock cmpxchg 指令

所以,cmpxchg本身是非原子性的

硬件:

lock指令在执行后面的时候锁定一个北桥信号,不采用锁总线的方式


对象在内存中的布局

new Object()在内存占多少字节

使用Open JDK的工具JOL(Java Object Layout),Maven导入

1
2
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());

输出:

image-20200726222016355

对象在内存中的布局:

image-20200726222735685

普通对象说明:

  • markword + 指针类型,加起来是对象头
  • 关于锁(synchronized)的信息都存在markword中
  • 指针类型指向所属的类
  • 实例数据存的是成员变量
  • 对齐是保证对象内存大小可以被8整除,使得总线读取更快

查看Java命令行参数:

1
2
3
4
5
6
$ java -XX:+PrintCommandLineFlags -version

-XX:G1ConcRefinementThreads=4 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:MinHeapSize=6815736 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
java version "14" 2020-03-17
Java(TM) SE Runtime Environment (build 14+36-1461)
Java HotSpot(TM) 64-Bit Server VM (build 14+36-1461, mixed mode)

对new Object()内存布局的解释:

  • 前两行:合计是markword,共8字节
  • 第三行,指针,指向类,4字节(补充:64位系统指针应该是8字节,但是被ClassPoniter压缩至4字节)
  • 第四行:补齐,由于不能被8整除,补齐4字节,即loss due to the next…
  • 由于没有成员变量,实例数据部分0字节

面试题:

1
Object o = new Object();在内存中占有多少字节? -- 顺丰

答:管理synchronized等信息的markword 8字节;如果开启了ClassPointer的压缩,类型指针占4字节;成员变量没有,所以0字节;合计12字节,由于不能被8整除,补齐至16字节。如果没有开启ClassPointer的压缩,类型指针占8个,那么就正好16字节,不需要补齐。


分析这段代码的内存布局,和之前的new Object()作个对比:

1
2
3
synchronized(o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}

输出:

image-20200726225404529

注意第一行value的区别,说明锁信息是保存在对象的markword内的。


锁升级:

new - 偏向锁 - 轻量级锁(无锁 == 自旋锁、自适应自旋)- 重量级锁

image-20200726233046984

由上图可知,锁状态、GC标记、分代年龄都是存在对象的markword的8字节内的

分代年龄:

每次GC,如果对象没有被回收,则分代年龄+1,达到一定值(PS+PO GC默认15; CMS GC默认6),则对象从年轻代到老年代

注意:

由于分代年龄是4bit表示的,最大值是1111(二进制),即1 + 2 + 4 + 8 = 15,所以将值调到大于15是没有用的

自旋锁进一步升级的条件:

  1. 有线程自旋超过10次
  2. 在自旋的线程超过总核数的1/2

自适应自旋锁可以自动调整以上的两个值


用户态和内核态

用户态:ring3 应用程序大多跑着用户态

内核态:ring0 申请锁需要在内核态进行

在内核态申请到锁之后,线程会进入一个阻塞的队列,wait的线程不消耗资源,区别于自旋锁依赖频繁的CAS会消耗大量CPU资源


锁降级:

锁降级只存在在GC的时候,其他线程已经不持有锁,只发生在VMThread访问的时候,所以没意义,一般认为不存在降级


锁消除

举例说明:

1
2
3
4
public void add(String str1, String str2) {
StringBuffer sb = new StringBuffer();
sb.append(str1).append(str2);
}

解释:

虽然StringBuffer是线程安全的,即append()方法是被synchronized修饰的,但是在本add方法内,锁会被消除。原因是,sb是局部变量而不是成员变量(栈私有),不可能存在竞争,所以sb对象的锁会被JVM消除。


锁粗化

举例说明:

1
2
3
4
5
6
7
8
9
public String test(String str) {
int i = 0;
StringBuffer sb = new StringBuffer();
while(i < 100) {
sb.append(str);
i++;
}
return sb.toString();
}

解释:

JVM检测到一连串对同一对象的加锁和解锁,JVM会粗化加锁范围,使得锁只需要加1次。

浅聊企业文化——读《浪潮之巅》有感

发表于 2020-05-25 | 分类于 感悟
本文字数: 2.4k | 阅读时长 ≈ 2 分钟

背景

在《浪潮之巅》第四版的下册中(原《硅谷之谜》),开篇第一章即是《挑战者——Google公司》。讨论Google总会给年轻人一种充满热血的感觉,因为这家企业确实是非常硅谷,非常叛逆。

阅读全文 »

Java有意思的现象——执行注释后的代码

发表于 2020-05-08 | 分类于 程序人生 > Java
本文字数: 241 | 阅读时长 ≈ 1 分钟

今天看到这样一个有意思的图片,给大家分享下:

image-20200508014429297

问题来了,为什么代码里面已经被注释的部分也可以执行呢?

阅读全文 »

HHKB使用心得

发表于 2020-05-07 | 分类于 程序人生 > 外设
本文字数: 205 | 阅读时长 ≈ 1 分钟

这篇文章主要是在使用HHKB键盘的过程中的一些心得和技巧,属于不定期追加内容的形式

阅读全文 »

Go实现最简单的超时控制

发表于 2020-05-07 | 分类于 程序人生 > Golang
本文字数: 314 | 阅读时长 ≈ 1 分钟
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"time"
)

func main() {
ch := make(chan int)

go func(ch chan int) {
time.Sleep(time.Second * 10)
ch <- 1
}(ch)

select {
case <-ch:
fmt.Println("ok")
case <-time.After(time.Second * 5):
fmt.Println("time out")
}
}
阅读全文 »
12…23
SherlockGy

SherlockGy

Keep thinking different...

111 日志
38 分类
62 标签
RSS
GitHub E-Mail
友情链接
  • GoLang-飞雪无情的博客
  • Java-酷壳
  • Python-静觅丨崔庆才的个人博客
  • 掘金
  • GoLang中国
  • GoLang中文社区
  • V2EX
  • Microsoft Docs
  • Go语言圣经
© 2020 SherlockGy | 191k | 2:false
由 Hexo 强力驱动 v3.7.1
|
主题 — NexT.Pisces v6.3.0