浅谈Java锁

每当遇到Java面试,“锁”是个必然会被提到的东西。那么,在面试中,谈“锁”都会谈论些什么呢,诸位看官又是否对“锁”有足够的了解?

本文旨在剖析锁的底层原理,以及锁的应用场景。

一、Synchronized

1、一道面试题

同一个对象在A、B两个线程中分别访问该对象的两个同步方法writer和reader,是否会产生互斥?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

class {

int a = 0;

public synchronized void writer(){

sleep(10);

a++;

}

public synchronized void reader(){

int i = a;

}

public static void main(String[] args) {

Test test = new Test();

new Thread(() -> {

test.writer();

}).start();

sleep(1);

new Thread(() -> {

test.reader();

}).start();

}

}

答案:会。因为synchronized修饰的是方法,锁是对象锁,默认当前的对象作为锁的对象。只有当A释放锁之后,B才会获得对象的锁。

(1)如果是换成是不同对象呢?

不会互斥,因为锁的是对象,而不是方法

(2)如果writer、reader方法加上static修饰,两个线程中,类直接调用两个方法呢?

会互斥,因为锁的是Class对象。

(3)如果writer方法用static修饰,reader方法不用呢?

不会互斥。因为一个是对象锁,一个是Class对象锁,锁的类型不同。

synchronized修饰位置与锁的关系

  • 同步方法 —— 对象锁,当前实例对象
  • 静态同步方法 —— 类对象锁,当前对象的Class对象
  • 同步方法块 —— 对象锁,synchonized括号里配置的对象

二、锁的底层实现

思考几个问题

  1. 对象锁、Class对象锁时如何实现的
  2. 为什么要这么设计,只设计一个对象锁或Class对象锁,有什么不好?

1、反编译

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

class {

static int a = 0;

public synchronized void writer() {

System.out.println("writer方法开始调用");

a++;

waitNs(20);

System.out.println("writer方法调用结束");

}

public  static synchronized void reader() {

System.out.println("reader方法开始调用");

int i = a;

System.out.println("reader方法调用结束");

}

public void writer2() {

synchronized (this) {

a--;

}

}

}

使用javacjavap -verbose命令,反编译上述代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117
...

{

static int a;

descriptor: I

flags: ACC_STATIC

public com.fonxian.entity.LockDemo();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokespecial #1                  // Method java/lang/Object."<init>":()V

4: return

LineNumberTable:

line 6: 0

//同步方法

public synchronized void writer();

descriptor: ()V

flags: ACC_PUBLIC, ACC_SYNCHRONIZED

Code:

stack=2, locals=1, args_size=1

0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;

3: ldc           #3                  // String writer方法开始调用

5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

8: getstatic     #5                  // Field a:I

11: iconst_1

12: iadd

13: putstatic     #5                  // Field a:I

16: bipush        20

18: invokestatic  #6                  // Method waitNs:(I)V

21: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;

24: ldc           #7                  // String writer方法调用结束

26: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

29: return

LineNumberTable:

line 11: 0

line 12: 8

line 13: 16

line 14: 21

line 15: 29

//静态同步方法

public static synchronized void reader();

descriptor: ()V

flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED

Code:

stack=2, locals=1, args_size=0

0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;

3: ldc           #8                  // String reader方法开始调用

5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

8: getstatic     #5                  // Field a:I

11: istore_0

12: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;

15: ldc           #9                  // String reader方法调用结束

17: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

20: return

LineNumberTable:

line 18: 0

line 19: 8

line 20: 12

line 21: 20

//同步代码块

public void writer2();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=2, locals=3, args_size=1

0: aload_0

1: dup

2: astore_1

3: monitorenter

4: getstatic     #5                  // Field a:I

7: iconst_1

8: isub

9: putstatic     #5                  // Field a:I

12: aload_1

13: monitorexit

14: goto          22

17: astore_2

18: aload_1

19: monitorexit

20: aload_2

21: athrow

22: return

Exception table:

from    to  target type

4    14    17   any

17    20    17   any

LineNumberTable:

line 25: 0

line 26: 4

line 27: 12

line 28: 22

StackMapTable: number_of_entries = 2

frame_type = 255 /* full_frame */

offset_delta = 17

locals = [ class com/fonxian/entity/LockDemo, class java/lang/Object ]

stack = [ class java/lang/Throwable ]

frame_type = 250 /* chop */

offset_delta = 4

static {};

descriptor: ()V

flags: ACC_STATIC

Code:

stack=1, locals=0, args_size=0

0: iconst_0

1: putstatic     #5                  // Field a:I

4: return

LineNumberTable:

line 8: 0

}

SourceFile: "LockDemo.java"

同步代码块:使用monitorenter和monitorexit指令实现,通过监听器对象去获得锁释放锁

同步方法、静态同步方法:使用修饰符ACC_SYNCHRONIZED实现。

三、锁的形式

JDK1.6之前,synchronized只有传统锁机制。
JDK1.6引入两种新的锁类型:偏向锁和轻量级锁。引入的目的是解决,没有多线程竞争或基本没有竞争的情况下,使用传统锁带来的性能问题。

锁的四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。锁可以升级,不能降级。

1、对象头

要了解锁的机制,首先要了解对象头。

Java对象头中的Mark Word默认存储对象的HashCode、分代年龄和锁标记位。

Java对象头的存储结构如下:

锁状态 25bit 4bit 1bit是否是偏向锁 2bit锁标志位
无锁状态 对象的hashCode 对象的分代年龄 0 01

Mark Word的状态变化:

锁状态 30bit 2bit
轻量级锁 指向栈中锁记录的指针 锁标志位00
重量级锁 指向互斥量的指针 锁标志位10
锁状态 23bit 3bit 3bit 1bit 2bit
偏向锁 线程ID Epoch 对象分代年龄 1 01

2、偏向锁

因大多数情况,锁不存在多线程竞争,且总由同一个线程多次获得。为使获得锁的代价更低而引入。

3、轻量级锁

参考文档

1、JDK8 HotSpot虚拟机源码

原文:大专栏  浅谈Java锁

原文地址:https://www.cnblogs.com/petewell/p/11614914.html

时间: 2024-10-10 09:37:11

浅谈Java锁的相关文章

搞懂分布式技术16:浅谈分布式锁的几种方案

搞懂分布式技术16:浅谈分布式锁的几种方案 前言 随着互联网技术的不断发展,数据量的不断增加,业务逻辑日趋复杂,在这种背景下,传统的集中式系统已经无法满足我们的业务需求,分布式系统被应用在更多的场景,而在分布式系统中访问共享资源就需要一种互斥机制,来防止彼此之间的互相干扰,以保证一致性,在这种情况下,我们就需要用到分布式锁. 分布式一致性问题 首先我们先来看一个小例子: 假设某商城有一个商品库存剩10个,用户A想要买6个,用户B想要买5个,在理想状态下,用户A先买走了6了,库存减少6个还剩4个,

浅谈 Java Printing

浅谈 Java  Printing 其实怎么说呢?在写这篇博文之前,我对java printing 可以说是一无所知的.以至于我在敲文字时, 基本上是看着api文档翻译过来的.这虽然看起来非常的吃力,但是我相信,有道大哥不会辜负我的.嘻 嘻! Java Printing 技术,也就是我们平时所接触的打印,只不过是说可以用Java实现而已. 一.Java Printing 打印简介 Java Printing API能够使java应用程序实现相关的打印功能,如: 1.打印所有 Java 2D 和

【转】浅谈Java中的equals和==

浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String("hello"); 3 4 System.out.println(str1==str2); 5 System.out.println(str1.equals(str2)); 为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初

浅谈java异常[Exception]

本文转自:focusJ 一. 异常的定义 在<java编程思想>中这样定义 异常:阻止当前方法或作用域继续执行的问题.虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常.绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败.之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意. 记得当初学习java的时候,异常总是搞不太清楚,不知道这个异常是什么意思,为什么会有这个机制?但是随着知识的积累逐渐也

浅谈Java虚拟机

最近发现MDT推出去的系统的有不同问题,其问题就不说了,主要是策略权限被域继承了.比如我们手动安装的很多东东都是未配置壮态,推的就默认为安全壮态了,今天细找了一下,原来把这个关了就可以了. 浅谈Java虚拟机,布布扣,bubuko.com

浅谈Java中的对象和引用

浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是"对象和对象引用",很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起了解一下对象和对象引用之间的区别和联系. 1.何谓对象? 在Java中有一句比较流行的话,叫做"万物皆对象",这是Java语言设计之初的理念之一.要理解什么是对象,需要跟类一起结合起来理解.下面这段话引自<Java编程思想>中的一段原话: "按照通

浅谈Java中的equals和==

浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String("hello"); 3 4 System.out.println(str1==str2); 5 System.out.println(str1.equals(str2)); 为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初

浅谈Java throw, throws, try catch异常处理

1.throws关键字通常被应用在声明方法时,用来指定可能抛出的异常.多个异常可以使用逗号隔开.当在主函数中调用该方法时,如果发生异常,就会将异常抛给指定异常对象.如下面例子所示: public class Shoot {   创建类 static void pop() throws NegativeArraySizeException { //定义方法并抛出NegativeArraySizeException异常 int [] arr = new int[-3];//创建数组 } public

浅谈Java中的hashcode方法 - 海 子

浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个方法的声明可知,该方法返回一个int类型的数值,并且是本地方法,因此在Object类中并没有给出具体的实现. 为何Object类需要这样一个方法?它有什么作用呢?今天我们就来具体探讨一下hashCode方法. 一.hashCode方法的作用 对于包含容器类型的程