2022年11月7日 16:04 周一转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
线程池
#
2022年11月6日 12:31 周日以下内容均转自
https://www.cnblogs.com/wuqinglong/p/9945618.html,部分疑惑参考自另一作者
https://github.com/farmerjohngit/myblog/issues/12 ,感谢原作者。
【目前还是存有部分疑虑(轻量级锁那块),可能需要详细看源码才能释疑】
概述
#
传统的synchronized为重量级锁(使用操作系统互斥量(mutex
)来实现的传统锁),但是随着JavaSE1.6对synchronized优化后,部分情况下他就没有那么重了。本文介绍了JavaSE1.6为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁结构、及锁升级过程
实现同步的基础
#
Java中每个对象都可以作为锁,具体变现形式
- 对于普通同步方法,锁是当前实例对象
- 对于静态同步方法,锁是当前类的Class对象
- 对于同步方法块,锁是synchronized括号里配置的对象
一个线程试图访问同步代码块时,必须获取锁;在退出或者抛出异常时,必须释放锁
实现方式
#
JVM 基于进入和退出 Monitor 对象来实现方法同步和代码块同步,但是两者的实现细节不一样
- 代码块同步:通过使用 monitorenter 和 monitorexit 指令实现的
- 同步方法:ACC_SYNCHRONIZED 修饰
monitorenter 指令是在编译后插入到同步代码块的开始位置,而 monitorexit 指令是在编译后插入到同步代码块的结束处或异常处
对于同步方法,进入方法前添加一个 monitorenter 指令,退出方法后添加一个 monitorexit 指令。
demo:
public class Demo {
public void f1() {
synchronized (Demo.class) {
System.out.println("Hello World.");
}
}
public synchronized void f2() {
System.out.println("Hello World.");
}
}
编译之后的字节码(使用 javap )
public void f1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // class me/snail/base/Demo
2: dup
3: astore_1
4: monitorenter
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #4 // String Hello World.
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return
Exception table:
from to target type
5 15 18 any
18 21 18 any
LineNumberTable:
line 6: 0
line 7: 5
line 8: 13
line 9: 23
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 18
locals = [ class me/snail/base/Demo, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
public synchronized void f2();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello World.
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 12: 0
line 13: 8
先说 f1() 方法,发现其中一个 monitorenter 对应了两个 monitorexit,这是不对的。但是仔细看 #15: goto 语句,直接跳转到了 #23: return 处,再看 #22: athrow 语句发现,原来第二个 monitorexit 是保证同步代码块抛出异常时锁能得到正确的释放而存在的,这就理解了。
...
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月28日 14:15 周五转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
JMM(JavaMemoryModel)
#
详见-知识点
![Java内存模型](img/ly-20241212141935173.png)
volatile关键字
#
2022年10月26日 16:46 周三转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!