Synchronized原理

1、Synchronized是什么

  Synchronized是Java中的关键字。

2、Synchronized的作用

  Synchronized可避免多线程同时操作临界资源,同一时间点,只会有一个线程操作临界资源,保证了操作的原子性。

3、Synchronized的使用

  Synchronized可以修饰静态方法、非静态方法、代码块。Synchronized示例详情:

 1 import java.util.concurrent.TimeUnit;
 2 
 3 public class TestSynchronized {
 4 
 5     private static int count;
 6 
 7     public static void main(String[] args) throws Exception {
 8         int finalIndex = 20;
 9         TestSynchronized testSynchronized = new TestSynchronized();
10         for (int i = 0; i < finalIndex; i++) {
11             new Thread(() -> {
12                 testSynchronized.add("没有synchronized修饰的添加方法");
13             }).start();
14         }
15         TimeUnit.SECONDS.sleep(2);
16         System.out.println("============================================================");
17         count = 0;
18         for (int i = 0; i < finalIndex; i++) {
19             new Thread(() -> {
20                 add01("synchronized修饰静态方法");
21             }).start();
22         }
23         TimeUnit.SECONDS.sleep(2);
24         System.out.println("============================================================");
25         count = 0;
26         for (int i = 0; i < finalIndex; i++) {
27             new Thread(() -> {
28                 testSynchronized.add02("synchronized修饰非静态方法");
29             }).start();
30         }
31         TimeUnit.SECONDS.sleep(2);
32         System.out.println("============================================================");
33         count = 0;
34         for (int i = 0; i < finalIndex; i++) {
35             new Thread(() -> {
36                 testSynchronized.add03("synchronized修饰代码块");
37             }).start();
38         }
39     }
40 
41     public void add(String msg) {
42         count++;
43         System.out.println(msg + " :" + count);
44     }
45 
46     public static  synchronized void add01(String msg) {
47         count ++;
48         System.out.println(msg + " :" + count);
49     }
50 
51     public synchronized void add02(String msg) {
52         count ++;
53         System.out.println(msg + " :" + count);
54     }
55 
56     public void add03(String msg) {
57         synchronized (TestSynchronized.class) {
58             count ++;
59             System.out.println(msg + " :" + count);
60         }
61     }
62 }

  运行后会发现,add方法的输出结果出现问题。add01、add02、add03方法的输出结果正常。

  首先count++是非原子的操作,在JVM字节码中的执行过程如下:

  

  下面来看看,Synchronized是如何保证原子性的。Synchronized修饰方法,会在对应方法上打上同步标识,ACC_SYNCHRONIZED:

  

 

  

   Synchronized修饰代码块,只有获得锁资源的线程可执行monitorenter指令后面的流程;在代码块执行结束或抛出异常时,执行monitorexit指令释放锁资源,以便其他线程争抢锁资源,

  monitorenter、monitorexit指令保证了同一时间点,只会有一个线程执行同步代码块中的内容。

  

4、Synchronized的优化

  Synchronized在JDK1.5做了锁消除、锁膨胀的优化,在JDK1.6做了锁升级的优化。

4.1、锁消除

  在synchronized修饰的代码块中,若不存在操作临界资源的情况,会触发锁消除,即便有synchronized修饰,也不会触发。

4.2、锁膨胀

  频繁的获取和释放资源,会将锁的范围扩大,减少性能的消耗。

4.3、锁升级

  Synchronized锁升级是在JDK1.6做的优化,在这之前Synchronized修饰的代码块,若线程未获取到锁,挂起当前线程。在JDK1.6做了锁升级的优化,无锁、偏向锁、轻量级锁、重量级锁。

5、Synchronized的实现原理

  Synchronized根据类锁或对象锁实现的。Synchronized修饰static静态方法,使用的是类锁,当前对象的class;Synchronized修饰非static静态方法,使用的是对象锁,当前对象this作为锁。synchronized是基于对象实现的,在JVM中,堆中的对象由对象头、实例数据、对齐填充这三部分构成。

  MarkWord中标记着四种锁的信息:无锁、偏向锁、轻量级锁、重量级锁。锁信息记录在对象的对象头Mard Word中,详情如下图所示:

锁状态说明:

1、无锁

  没有线程操作临界资源。

2、偏向锁

  若当前资源,仅有一个线程在获取和释放锁资源,当该线程操作临界资源,只需判断指向的线程是否为当前线程。若为当前线程,成功获取锁资源;若不是当前线程,那就是出现了锁竞争,通过CAS的方式尝试将  锁指向当前线程,若获取不到锁资源,触发锁升级,升级成轻量级锁。

3、轻量级锁

  自旋锁的方式通过CAS的方式获取锁资源,若CAS成功,获取到锁资源;若CAS一直失败,自旋到一定的次数,还没有获取到锁资源,进行锁升级,升级重量级锁。

4、重量级锁

  拿不到锁资源,挂起当前线程。涉及用户态、内核态的切换,耗性能。

 

posted @ 2023-04-20 14:52  无虑的小猪  阅读(45)  评论(0编辑  收藏  举报