2022年11月3日 11:08 周四本文主要讲解synchronized原理和偏向锁、轻量级锁、重量级锁的升级过程,基本都转自
https://blog.csdn.net/MariaOzawa/article/details/107665689 原作者:
MariaOzawa
简介
#
- 为什么需要锁
并发编程中,多个线程访问同一共享资源时,必须考虑如何维护数据的原子性 - 历史
- JDK1.5之前,Java依靠Synchronized关键字实现锁功能,Synchronized是Jvm实现的内置锁,锁的获取与释放由JVM隐式实现
- JDK1.5,并发包新增Lock接口实现锁功能,提供同步功能,使用时显式获取和释放锁
- 区别
- Lock同步锁基于Java实现,Synchronized基于底层操作系统的MutexLock实现
/ˈmjuːtɛks/
,每次获取和释放锁都会带来用户态和内核态的切换,从而增加系统性能开销,性能糟糕,又称重量级锁 - JDK1.6之后,对Synchronized同步锁做了充分优化
Synchronized同步锁实现原理
#
Synchronized实现同步锁的两种方式:修饰方法;修饰方法块
// 关键字在实例方法上,锁为当前实例
public synchronized void method1() {
// code
}
// 关键字在代码块上,锁为括号里面的对象
public void method2() {
Object o = new Object();
synchronized (o) {
// code
}
}
这里使用编译–及javap 打印字节文件
javac -encoding UTF-8 SyncTest.java //先运行编译class文件命令
javap -v SyncTest.class //再通过javap打印出字节文件
结果如下,Synchronized修饰代码块时,由monitorenter和monitorexist指令实现同步。进入monitorenter指令后线程持有Monitor对象;退出monitorenter指令后,线程释放该Monitor对象
public void method2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: new #2
3: dup
4: invokespecial #1
7: astore_1
8: aload_1
9: dup
10: astore_2
11: monitorenter //monitorenter 指令
12: aload_2
13: monitorexit //monitorexit 指令
14: goto 22
17: astore_3
18: aload_2
19: monitorexit
20: aload_3
21: athrow
22: return
Exception table:
from to target type
12 14 17 any
17 20 17 any
LineNumberTable:
line 18: 0
line 19: 8
line 21: 12
line 22: 22
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 17
locals = [ class com/demo/io/SyncTest, class java/lang/Object, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
如果Synchronized修饰同步方法,代替monitorenter和monitorexit的是 ACC_SYNCHRONIZED
标志,即:JVM使用该访问标志区分方法是否为同步方法。方法调用时,调用指令检查是否设置ACC_SYNCHRONIZED标志,如有,则执行线程先持有该Monitor对象,再执行该方法;运行期间,其他线程无法获取到该Monitor对象;方法执行完成后,释放该Monitor对象
javap -v xx.class 字节文件查看
...
2022年10月31日 11:08 周一简介
#
无锁 => 偏向锁 => 轻量锁 => 重量锁
复习Class类锁和实例对象锁,说明Class类锁和实例对象锁不是同一把锁,互相不影响
public static void main(String[] args) throws InterruptedException {
Object object=new Object();
new Thread(()->{
synchronized (Customer.class){
System.out.println(Thread.currentThread().getName()+"Object.class类锁");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"结束并释放锁");
},"线程1").start();
//保证线程1已经获得类锁
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
synchronized (object){
System.out.println(Thread.currentThread().getName()+"获得object实例对象锁");
}
System.out.println(Thread.currentThread().getName()+"结束并释放锁");
},"线程2").start();
}
/* 输出
线程1Object.class类锁
线程2获得object实例对象锁
线程2结束并释放锁
线程1结束并释放锁
*/
总结图 , 00 , 01 , 10 ,没有11
...
2022年10月30日 16:56 周日对象布局
#
heap (where): new (eden ,s0 ,s1) ,old, metaspace
对象的构成元素(what)
HotSpot虚拟机里,对象在堆内存中的存储布局分为三个部分

- 对象头(Header)
- 对象标记 MarkWord
- 类元信息(类型指针 Class Pointer,指向方法区的地址)
- 对象头多大 length(数组才有)
- 实例数据(Instance Data)
- 对其填充(Padding,保证整个对象大小,是8个字节的倍数)
对象头
#
实例数据和对齐填充
#
源码查看
#

...
2022年10月28日 14:15 周五转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
JMM(JavaMemoryModel)
#
详见-知识点

volatile关键字
#
2022年10月27日 16:46 周四- springCloud涉及到的技术有哪些

- 约定 > 配置 > 编码
2022年10月26日 16:46 周三转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
2022年10月26日 14:17 周三转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
https://zhuanlan.zhihu.com/p/360878783 IO多路复用讲解,这是一个与系统底层有关的知识点,需要一些操作系统调用代码才知道IO多路复用省的时间。
I/O
#
何为I/O
#
- I/O(Input/Output),即输入/输出
从计算机结构的角度来解读一下I/O,根据冯诺依曼结构,计算机结构分为5大部分:运算器、控制器、存储器、输入设备、输出设备
其中,输入设备:键盘;输出设备:显示器
网卡、硬盘既属于输入设备也属于输出设备 - 输入设备向计算机输入(内存)数据,输出设备接收计算机(内存)输出的数据,即I/O描述了计算机系统与外部设备之间通信的过程
- 从应用程序的角度解读I/O
- 为了保证系统稳定性和安全性,一个进程的地址空间划分为用户空间User space和内核空间Kernel space
kernel 英[ˈkɜːnl]
- 平常运行的应用程序都运行在用户空间,只有内核空间才能进行系统态级别的资源有关操作—文件管理、进程通信、内存管理
- 如果要进行IO操作,就得依赖内核空间的能力,用户空间的程序不能直接访问内核空间
- 用户进程要想执行IO操作,必须通过系统调用来间接访问内核空间
- 对于磁盘IO(读写文件)和网络IO(网络请求和响应),从应用程序视角来看,应用程序对操作系统的内核发起IO调用(系统调用),操作系统负责的内核执行具体IO操作
- 应用程序只是发起了IO操作调用,而具体的IO执行则由操作系统内核完成
- 应用程序发起I/O后,经历两个步骤
- 内核等待I/O设备准备好数据
- 内核将数据从内核空间拷贝到用户空间
有哪些常见的IO模型
#
UNIX系统下,包括5种:同步阻塞I/O,同步非阻塞I/O,I/O多路复用、信号驱动I/O和异步I/O
Java中3中常见I/O模型
#
BIO (Blocking I/O )
#
- 应用程序发起read调用后,会一直阻塞,直到内核把数据拷贝到用户空间

NIO (Non-blocking/New I/O)
#
- 对于java.nio包,提供了Channel、Selector、Buffer等抽象概念,对于高负载高并发,应使用NIO
- NIO是I/O多路复用模型,属于同步非阻塞IO模型
一般的同步非阻塞 IO 模型中,应用程序会一直发起 read 调用。
等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的**,**直到在内核把数据拷贝到用户空间。
相比于同步阻塞 IO 模型,同步非阻塞 IO 模型确实有了很大改进。通过轮询操作,避免了一直阻塞。
但是,这种 IO 模型同样存在问题:应用程序不断进行 I/O 系统调用轮询数据是否已经准备好的过程是十分消耗 CPU 资源的。
★★ 也就是说,【准备数据,数据就绪】是不阻塞的。而【拷贝数据】是阻塞的

...
2022年10月24日 23:40 周一转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
装饰器模式
#
类图:

装饰器,Decorator,装饰器模式可以在不改变原有对象的情况下拓展其功能
★装饰器模式,通过组合替代继承来扩展原始类功能,在一些继承关系较复杂的场景(IO这一场景各种类的继承关系就比较复杂)下更加实用
对于字节流,FilterInputStream(对应输入流)和FilterOutputStream(对应输出流)是装饰器模式的核心,分别用于增强(继承了)InputStream和OutputStream子类对象的功能
Filter (过滤的意思),中间(Closeable)下面这两条虚线代表实现;最下面的实线代表继承

其中BufferedInputStream(字节缓冲输入流)、DataInputStream等等都是FilterInputStream的子类,对应的BufferedOutputStream和DataOutputStream都是FilterOutputStream的子类
例子,使用BufferedInputStream(字节缓冲输入流)来增强FileInputStream功能
ZipInputStream和ZipOutputStream还可以用来增强BufferedInputStream和BufferedOutputStream的能力
...
2022年10月23日 12:21 周日转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
简介
#
字节流
#
2022年10月22日 18:26 周六转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
总结
#
Java7 中 ConcurrentHashMap
使用的分段锁,也就是每一个 Segment 上同时只有一个线程可以操作,每一个 Segment
都是一个类似 HashMap
数组的结构,每一个HashMap可以扩容,它的冲突会转化为链表。但是 Segment
的个数一但初始化就不能改变。
Java8 中的 ConcurrentHashMap
使用的 Synchronized
锁加 CAS 的机制。结构也由 Java7 中的 Segment
数组 + HashEntry
数组 + 链表 进化成了 Node 数组 + 链表 / 红黑树,Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表。
源码 (略过)
#
ConcurrentHashMap1.7
#
- 存储结构
- Segment数组(该数组用来加锁,每个数组元素是一个HashEntry数组(该数组可能包含链表)
- 如图,ConcurrentHashMap由多个Segment组合,每一个Segment是一个类似HashMap的结构,每一个HashMap内部可以扩容,但是Segment个数初始化后不能改变,默认16个(即默认支持16个线程并发)

ConcurrentHashMap1.8
#