java并发synchronized 锁的膨胀过程(锁的升级过程)深入剖析(2)

接下来我们分析两个批量偏向撤销的相关案例(禁止偏向锁延迟的情况下:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0):

案例一:

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

public class TestDemo {

}

  

public class DemoExample6 {

    public static void main(String[] args) throws InterruptedException {

        test2();

    }

   

    /**

     * 证明偏量偏向撤销

     * @throws InterruptedException

     */

    public  static  void test2() throws InterruptedException {

        List<TestDemo> list = new ArrayList<TestDemo>();

        for (int i = 0; i < 100; i++) {

            list.add(new TestDemo());

        }

        Thread t1 = new Thread(()->{

            System.out.println("加锁前 get(0) 应该是无锁可偏向 "+ClassLayout.parseInstance(list.get(0)).toPrintable());

            for (TestDemo a:list  ) {

                synchronized (a){

                    System.out.print("加锁 >");

                }

            }

            System.out.println();

            System.out.println("加锁后 get(0) 应该是偏向锁"+ClassLayout.parseInstance(list.get(0)).toPrintable());

            try {

                TimeUnit.SECONDS.sleep(1000);//这里不让线程死,防止线程ID复用

            catch (InterruptedException e) {

                e.printStackTrace();

            }

        });

        t1.start();

        TimeUnit.SECONDS.sleep(5);

        Thread t2 = new Thread(()->{

            for (int i = 0; i < 100; i++) {

                TestDemo a = list.get(i);

                synchronized (a){

                    System.out.println(Thread.currentThread().getId()+"加锁 >");

                }

                try {

                    TimeUnit.MILLISECONDS.sleep(100);

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

                if (i==9){//这里刚好是第19个上锁的(同样是第19个偏向锁升级的)

                    System.out.println();

                    System.out.println("加锁后 get(9) 应该是无锁(轻量级锁释放) "+ClassLayout.parseInstance(list.get(i)).toPrintable());

                }

                if (i==10){//这里刚好是第21个上锁的

                    System.out.println();

                    System.out.println("加锁后 get(10) 应该是偏向锁 偏向t2 "+ClassLayout.parseInstance(list.get(i)).toPrintable());

                }

                if (i==50){//50开始升级为轻量级锁(同样是第21个偏向锁升级的)

                    System.out.println();

                    System.out.println("加锁后 get(50) 无锁(轻量级锁释放) "+ClassLayout.parseInstance(list.get(i)).toPrintable());

                }

                if (i==59){//60(同样是第39个偏向锁升级的)

                    System.out.println();

                    System.out.println("加锁后 get(59) 无锁(轻量级锁释放) "+ClassLayout.parseInstance(list.get(i)).toPrintable());

                }

                if (i==69){//69(同样是第59个偏向锁升级的)

                    System.out.println();

                    System.out.println("加锁后 get(69) 无锁(轻量级锁释放) "+ClassLayout.parseInstance(list.get(i)).toPrintable());

                    TestDemo a1 = new TestDemo();

                    synchronized (a1){

                        System.out.println("偏向撤销发生后的该类新建的对象都不会再偏向任何线程 "+ClassLayout.parseInstance(a1).toPrintable());

                    }

                }

            }

        });

   

        Thread t3 = new Thread(()->{

            for (int i = 99; i >= 0; i--) {

                TestDemo a = list.get(i);

                synchronized (a){

                    System.out.println(Thread.currentThread().getId()+"加锁 >");

                }

                try {

                    TimeUnit.MILLISECONDS.sleep(100);

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

                /**

                 * 重点:重偏向撤销

                 */

                if (i==40){//40升级为轻量级锁(同样是第40个偏向锁升级的,这时候发生偏向撤销)

                    System.out.println();

                    System.out.println("加锁后 get("+i+") 应该是无锁(轻量级锁释放) "+ClassLayout.parseInstance(list.get(0)).toPrintable());

                    TestDemo a1 = new TestDemo();

                    synchronized (a1){

                        System.out.println("偏向撤销发生后的该类新建的对象都不会再偏向任何线程 "+ClassLayout.parseInstance(a1).toPrintable());

                    }

                }

                if (i==30){//39升级为轻量级锁(同样是第42个偏向锁升级的)

                    System.out.println();

                    System.out.println("加锁后 get("+i+") 应该是无锁(轻量级锁释放) "+ClassLayout.parseInstance(list.get(0)).toPrintable());

                    TestDemo a1 = new TestDemo();

                    synchronized (a1){

                        System.out.println("偏向撤销发生后的该类新建的对象都不会再偏向任何线程 "+ClassLayout.parseInstance(a1).toPrintable());

                    }

                }

            }

        });

        t2.start();

        TimeUnit.MILLISECONDS.sleep(50);

        t3.start();

    }

}  

 

运行结果(截取部分):

加锁前 get(0) 应该是无锁可偏向 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >加锁 >

 

加锁后 get(0) 应该是偏向锁com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 e0 84 08 (00000101 11100000 10000100 00001000) (142925829)

      4     4        (object header)                           b1 7f 00 00 (10110001 01111111 00000000 00000000) (32689)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

加锁后 get(9) 应该是无锁(轻量级锁释放) com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

15加锁 >

 

 

加锁后 get(90) 应该是偏向锁 偏向t3com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 89 01 0c (00000101 10001001 00000001 00001100) (201427205)

      4     4        (object header)                           b1 7f 00 00 (10110001 01111111 00000000 00000000) (32689)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

加锁后 get(10) 应该是偏向锁 偏向t2 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 b1 0a 08 (00000101 10110001 00001010 00001000) (134918405)

      4     4        (object header)                           b1 7f 00 00 (10110001 01111111 00000000 00000000) (32689)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

15加锁 >

 

 

加锁后 get(89) 应该是无锁(轻量级锁释放) com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

加锁后 get(50) 无锁(轻量级锁释放) com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

15加锁 >

  

加锁后 get(49) 应该是无锁(轻量级锁释放) com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

加锁后 get(59) 无锁(轻量级锁释放) com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

  

15加锁 > 

 

加锁后 get(40) 应该是无锁(轻量级锁释放) com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

  

偏向撤销发生后的该类新建的对象都不会再偏向任何线程 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           48 18 a6 09 (01001000 00011000 10100110 00001001) (161880136)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

加锁后 get(69) 无锁(轻量级锁释放) com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

  

偏向撤销发生后的该类新建的对象都不会再偏向任何线程 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           50 e8 95 09 (01010000 11101000 10010101 00001001) (160819280)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

  

15加锁 >

 

加锁后 get(30) 应该是无锁(轻量级锁释放) com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

  

偏向撤销发生后的该类新建的对象都不会再偏向任何线程 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           48 18 a6 09 (01001000 00011000 10100110 00001001) (161880136)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

案例二:

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

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

public class TestDemo {

}

public class DemoExample8 {

    public static void main(String[] args) throws Exception {

        List<TestDemo> list = new ArrayList<>();

        List<TestDemo> list2 = new ArrayList<>();

        List<TestDemo> list3 = new ArrayList<>();

        for (int i = 0; i < 100; i++) {

            list.add(new TestDemo());

            list2.add(new TestDemo());

            list3.add(new TestDemo());

        }

        //偏向锁

        System.out.println("初始状态" 10 + ClassLayout.parseClass(TestDemo.class).toPrintable());

   

        Thread t1 = new Thread() {

            String name = "1";

            public void run() {

                System.out.printf(name);

                for (TestDemo a : list) {

                    synchronized (a) {

                        if (a == list.get(10)) {

                            //偏向锁

                            System.out.println("t1 预期是偏向锁" 10 + ClassLayout.parseInstance(a).toPrintable());

                        }

                    }

                }

                try {

                    Thread.sleep(100000);

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        };

        t1.start();

        Thread.sleep(5000);

        //偏向锁

        System.out.println("main 预期是偏向锁" 10 + ClassLayout.parseInstance(list.get(10)).toPrintable());

        Thread t2 = new Thread() {

            String name = "2";

            public void run() {

                System.out.printf(name);

                for (int i = 0; i < 100; i++) {

                    TestDemo a = list.get(i);

                    synchronized (a) {

                        if (a == list.get(10)) {

                            System.out.println("t2 i=10 get(1)预期是无锁" + ClassLayout.parseInstance(list.get(1)).toPrintable());//偏向锁

                            System.out.println("t2 i=10 get(10) 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                        }

                        if (a == list.get(19)) {

                            System.out.println("t2  i=19  get(10)预期是无锁" 10 + ClassLayout.parseInstance(list.get(10)).toPrintable());//偏向锁

                            System.out.println("t2  i=19  get(19) 满足重偏向条件20 预期偏向锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                            System.out.println("类的对象累计撤销达到20");

                        

                    }

                }

                try {

                    Thread.sleep(100000);

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        };

        t2.start();

        Thread.sleep(5000);

  

        Thread t3 = new Thread() {

            String name = "3";

            public void run() {

                System.out.printf(name);

                for (TestDemo a : list2) {

                    synchronized (a) {

                        if (a == list2.get(10)) {

                            System.out.println("t3 预期是偏向锁" 10 + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                        }

                    }

                }

                try {

                    Thread.sleep(100000);

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        };

        t3.start();

        Thread.sleep(5000);

  

        Thread t4 = new Thread() {

            String name = "4";

            public void run() {

                System.out.printf(name);

                for (int i = 0; i < 100; i++) {

                    TestDemo a = list2.get(i);

                    synchronized (a) {

                        if (a == list2.get(10)) {

                            System.out.println("t4 i=10 get(1)预期是无锁" + ClassLayout.parseInstance(list2.get(1)).toPrintable());//偏向锁

                            System.out.println("t4 i=10 get(10) 当前不满足重偏向条件 20 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                        }

                        if (a == list2.get(19)) {

                            System.out.println("t4  i=19  get(10)预期是无锁" 10 + ClassLayout.parseInstance(list2.get(10)).toPrintable());//偏向锁

                            System.out.println("t4 i=19 get(19) 当前满足重偏向条件 20 但A类的对象累计撤销达到40 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                            System.out.println("类的对象累计撤销达到40");

                        }

                        if (a == list2.get(20)) {

                            System.out.println("t4 i=20 get(20) 当前满足重偏向条件 20 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                        }

                    }

                }

            }

        };

        t4.start();

        Thread.sleep(5000);

        System.out.println("main 预期是偏向锁" 10 + ClassLayout.parseInstance(list3.get(0)).toPrintable());//偏向锁

        Thread t5 = new Thread() {

            String name = "5";

            public void run() {

                System.out.printf(name);

                for (TestDemo a : list3) {

                    synchronized (a) {

                        if (a == list3.get(10)) {

                            System.out.println("t5 预期是轻量级锁,类的对象累计撤销达到40 不可以用偏向锁了" 10 + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                        }

                    }

                }

                try {

                    Thread.sleep(100000);

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        };

        t5.start();

        Thread.sleep(5000);

        System.out.println("main 预期是偏向锁" 10 + ClassLayout.parseInstance(list.get(10)).toPrintable());//偏向锁

  

        Thread t6 = new Thread() {

            String name = "6";

            public void run() {

                System.out.printf(name);

                for (int i = 0; i < 100; i++) {

                    TestDemo a = list3.get(i);

                    synchronized (a) {

                        if (a == list3.get(10)) {

                            System.out.println("t6 i=10 get(1)预期是无锁" + ClassLayout.parseInstance(list3.get(1)).toPrintable());//偏向锁

                            System.out.println("t6 i=10 get(10) 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                        }

                        if (a == list3.get(19)) {

                            System.out.println("t6  i=19  get(10)预期是无锁" 10 + ClassLayout.parseInstance(list3.get(10)).toPrintable());//偏向锁

                            System.out.println("t6  i=19  get(19) 满足重偏向条件20 但类的对象累计撤销达到40 不可以用偏向锁了 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁

                        }

                    }

                }

  

                try {

                    Thread.sleep(100000);

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        };

        t6.start();

        Thread.sleep(5000);

  

        System.out.println("由于撤销锁次数达到默认的 BiasedLockingBulkRevokeThreshold=40 这里实例化的对象 是无锁状态" + ClassLayout.parseInstance(new TestDemo()).toPrintable());//偏向锁

     System.out.println("撤销偏向后状态" 10 + ClassLayout.parseInstance(new TestDemo()).toPrintable());//偏向锁

  }

}

   

运行结果:

初始状态10 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0    12        (object header)                           N/A

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

1t1 预期是偏向锁10 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 e0 86 8e (00000101 11100000 10000110 10001110) (-1903763451)

      4     4        (object header)                           ec 7f 00 00 (11101100 01111111 00000000 00000000) (32748)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

main 预期是偏向锁10 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 e0 86 8e (00000101 11100000 10000110 10001110) (-1903763451)

      4     4        (object header)                           ec 7f 00 00 (11101100 01111111 00000000 00000000) (32748)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

2t2 i=10 get(1)预期是无锁com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t2 i=10 get(10) 预期轻量级锁 10 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           08 99 7a 03 (00001000 10011001 01111010 00000011) (58366216)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t2  i=19  get(10)预期是无锁10 com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t2  i=19  get(19) 满足重偏向条件20 预期偏向锁 19com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 09 90 91 (00000101 00001001 10010000 10010001) (-1852831483)

      4     4        (object header)                           ec 7f 00 00 (11101100 01111111 00000000 00000000) (32748)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

类的对象累计撤销达到20

3t3 预期是偏向锁10com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 09 89 90 (00000101 00001001 10001001 10010000) (-1870067451)

      4     4        (object header)                           ec 7f 00 00 (11101100 01111111 00000000 00000000) (32748)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

4t4 i=10 get(1)预期是无锁com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t4 i=10 get(10) 当前不满足重偏向条件 20 预期轻量级锁 10com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           08 f9 9a 03 (00001000 11111001 10011010 00000011) (60487944)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t4  i=19  get(10)预期是无锁10com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t4 i=19 get(19) 当前满足重偏向条件 20 但A类的对象累计撤销达到40 预期轻量级锁 19com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           08 f9 9a 03 (00001000 11111001 10011010 00000011) (60487944)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

类的对象累计撤销达到40

t4 i=20 get(20) 当前满足重偏向条件 20 预期轻量级锁 20com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           08 f9 9a 03 (00001000 11111001 10011010 00000011) (60487944)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

main 预期是偏向锁10com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

5t5 预期是轻量级锁,A类的对象累计撤销达到40 不可以用偏向锁了10com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           08 f9 9a 03 (00001000 11111001 10011010 00000011) (60487944)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

main 预期是偏向锁10com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

6t6 i=10 get(1)预期是无锁com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t6 i=10 get(10) 预期轻量级锁 10com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           08 29 ab 03 (00001000 00101001 10101011 00000011) (61548808)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t6  i=19  get(10)预期是无锁10com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t6  i=19  get(19) 满足重偏向条件20 但A类的对象累计撤销达到40 不可以用偏向锁了 19com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           08 29 ab 03 (00001000 00101001 10101011 00000011) (61548808)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

由于类撤销锁次数达到默认的 BiasedLockingBulkRevokeThreshold=40 这里实例化的对象 是无锁状态com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8     4        (object header)                           bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

撤销偏向后状态10com.boke.TestDemo object internals:
OFFSET SIZE TYPE DESCRIPTION                     VALUE
  0    4        (object header)                01 00 00 00 (00000001 00000000 00000000 00000000) (1)
  4    4        (object header)               00 00 00 00 (00000000 00000000 00000000 00000000) (0)
  8    4        (object header)                bf c3 00 f8 (10111111 11000011 00000000 11111000) (-134167617)
  12   4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

以上案例证实了偏向锁的批量重偏向和批量撤销,接下来我们讲解轻量级锁;

 


 

轻量级锁:

 

  • 当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。
  • 在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,然后拷贝对象头中的Mark Word复制到锁记录中。
  • 拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock Record里的owner指针指向对象的Mark Word。
  • 如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,表示此对象处于轻量级锁定状态。
  • 如果轻量级锁的更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明多个线程竞争锁。
  • 若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。
  • 多个线程在不同的时间段请求同一把锁,也就是说没有锁竞争。针对这种情形,Java 虚拟机采用了轻量级锁,来避免重量级锁的阻塞以及唤醒
  • 在没有锁竞争的前提下,减少传统锁使用OS互斥量产生的性能损耗
  • 在竞争激烈时,轻量级锁会多做很多额外操作,导致性能下降
  • 可以认为两个线程交替执行的情况下请求同一把锁

 

分析一个由偏向锁膨胀成轻量级锁的案例:

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

public class TestDemo {

}

public class DemoExample9 {

    public static void main(String[] args) throws Exception {

        TestDemo testDemo = new TestDemo();

  

        //子线程

        Thread t1 = new Thread(){

            @Override

            public void run() {

                synchronized (testDemo){

                    System.out.println("t1 lock ing");

                    System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());

                }

            }

        };

         

        t1.join();

  

        //主线程

        synchronized (testDemo){

            System.out.println("main lock ing");

            System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());

        }

  

    }

}

  

运行结果(两个线程交替执行的情况下):

main lock ing

com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           e8 48 95 09 (11101000 01001000 10010101 00001001) (160778472)

      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

      8     4        (object header)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 


 

重量级锁:

 

  • 多个线程竞争同一个锁的时候,虚拟机会阻塞加锁失败的线程,并且在目标锁被释放的时候,唤醒这些线程;
  • Java 线程的阻塞以及唤醒,都是依靠操作系统来完成的:os pthread_mutex_lock() ;
  • 升级为重量级锁时,锁标志的状态值变为“10”,此时Mark Word中存储的是指向重量级锁的指针,此时等待锁的线程都会进入阻塞状态

  

分析一个由轻量级锁膨胀成重量级锁的案例:

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

public class TestDemo {

}

public class DemoExample9 {

    public static void main(String[] args) throws Exception {

        TestDemo testDemo = new TestDemo();

  

  

        Thread t1 = new Thread(){

            @Override

            public void run() {

                synchronized (testDemo){

                    System.out.println("t1 lock ing");

                    System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());

                }

            }

        };

  

        t1.start();

  

        synchronized (testDemo){

            System.out.println("main lock ing");

            System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());

        }

    }

}

 

 运行结果:

main lock ing

com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           5a ad 00 b0 (01011010 10101101 00000000 10110000) (-1342132902)

      4     4        (object header)                           cf 7f 00 00 (11001111 01111111 00000000 00000000) (32719)

      8     4        (object header)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)

     12     4        (loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

t1 lock ing

com.boke.TestDemo object internals:

OFFSET  SIZE   TYPE DESCRIPTION                               VALUE

      0     4        (object header)                           5a ad 00 b0 (01011010 10101101 00000000 10110000) (-1342132902)

      4     4        (object header)                           cf 7f 00 00 (11001111 01111111 00000000 00000000) (32719)

      8     4        (object header)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)

     12     4        (loss due to the next object alignment)


 

我们再来说一下Java 虚拟机是怎么区分轻量级锁和重量级锁的:

  • 当进行加锁操作时,Java 虚拟机会判断是否已经是重量级锁。如果不是,它会在当前线程的当前栈桢中划出一块空间,作为该锁的锁记录,并且将锁对象的标记字段复制到该锁记录中。

  • 然后,Java 虚拟机会尝试用 CAS(compare-and-swap)操作替换锁对象的标记字段。这里解释一下,CAS 是一个原子操作,它会比较目标地址的值是否和期望值相等,如果相等,则替换为一个新的值。

  • 假设当前锁对象的标记字段为 X…XYZ,Java 虚拟机会比较该字段是否为 X…X01。如果是,则替换为刚才分配的锁记录的地址。由于内存对齐的缘故,它的最后两位为 00。此时,该线程已成功获得这把锁,可以继续执行了。

  • 如果不是 X…X01,那么有两种可能。第一,该线程重复获取同一把锁。此时,Java 虚拟机会将锁记录清零,以代表该锁被重复获取。第二,其他线程持有该锁。此时,Java 虚拟机会将这把锁膨胀为重量级锁,并且阻塞当前线程。

  • 当进行解锁操作时,如果当前锁记录(你可以将一个线程的所有锁记录想象成一个栈结构,每次加锁压入一条锁记录,解锁弹出一条锁记录,当前锁记录指的便是栈顶的锁记录)的值为 0,则代表重复进入同一把锁,直接返回即可。

  • 否则,Java 虚拟机会尝试用 CAS 操作,比较锁对象的标记字段的值是否为当前锁记录的地址。如果是,则替换为锁记录中的值,也就是锁对象原本的标记字段。此时,该线程已经成

  • 功释放这把锁。

  • 如果不是,则意味着这把锁已经被膨胀为重量级锁。此时,Java 虚拟机会进入重量级锁的释放过程,唤醒因竞争该锁而被阻塞了的线程

 


 

到此为止本篇就讲完了锁的膨胀过程

 

 

总结一下

  1. 偏向锁只会在第一次请求时采用 CAS 操作,在锁对象的标记字段中记录下当前线程的地址。在之后的运行过程中,持有该偏向锁的线程的加锁操作将直接返回。它针对的是锁仅会被同一线程持有的情况。

  2. 轻量级锁采用 CAS 操作,将锁对象的标记字段替换为一个指针,指向当前线程栈上的一块空间,存储着锁对象原本的标记字段。它针对的是多个线程在不同时间段申请同一把锁的情况。

  3. 重量级锁会阻塞、唤醒请求加锁的线程。它针对的是多个线程同时竞争同一把锁的情况。Java 虚拟机采取了自适应自旋,来避免线程在面对非常小的 synchronized 代码块时,仍会被阻塞、唤醒的情况。

 


 

 

说完了锁的膨胀过程,那么会不会有锁的降级呢?

 

我在hotspot源码中找到了这样的注释:

// We create a list of in-use monitors for each thread.

//

// deflate_thread_local_monitors() scans a single thread's in-use list, while

// deflate_idle_monitors() scans only a global list of in-use monitors which

// is populated only as a thread dies (see omFlush()).

//

// These operations are called at all safepoints, immediately after mutators

// are stopped, but before any objects have moved. Collectively they traverse

// the population of in-use monitors, deflating where possible. The scavenged

// monitors are returned to the monitor free list.

//

// Beware that we scavenge at *every* stop-the-world point. Having a large

// number of monitors in-use could negatively impact performance. We also want

// to minimize the total # of monitors in circulation, as they incur a small

// footprint penalty.

//

// Perversely, the heap size -- and thus the STW safepoint rate --

// typically drives the scavenge rate.  Large heaps can mean infrequent GC,

// which in turn can mean large(r) numbers of objectmonitors in circulation.

// This is an unfortunate aspect of this design.

//大概意思是:锁降级确实是会发生的,当 JVM 进入安全点(SafePoint)的时候,会检查是否有闲置的 Monitor,然后试图进行降级

有兴趣的大佬可以在https://hg.openjdk.java.net/jdk/jdk/file/896e80158d35/src/hotspot/share/runtime/synchronizer.cpp链接中:

研究一下deflate_idle_monitors是分析锁降级逻辑的入口,这部分行为还在进行持续改进,因为其逻辑是在安全点内运行,处理不当可能拖长 JVM 停顿(STW,stop-the-world)的时间。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值