Synchronize原理

Synchronized锁机制解析
本文深入探讨了Java中Synchronized锁的工作原理,包括其在字节码层面的实现,对象头中MarkWord的作用,以及偏向锁和轻量级锁的机制。通过分析单例模式下Synchronized的使用,揭示了其在多线程环境下的性能表现。

Synchronize原理

1 普通方法上

2 静态方法上

 

 修饰静态方法内置锁是当前的Class字节码对象

 修饰普通方法内置锁是当前类的实例

 

 

原理与使用:

   从字节码层面解释:

     执行同步代码块

     monitorenter 

     synchronized( ){

 

 

     }

   monitorexit  

 

任何对象都可以作为锁,那么锁信息有存在对象的什么地方呢? 

   存在对象头中

对象头中的信息    Mark Word   Class Metadata Address   ArrayLength

Mark Word 存储的是哈希值 锁信息  空间利用率很高的

 

任何对象都可以作为锁,那么锁信息又存在对象的什么地方呢? 

   存在对象头中

      对象头中的信息: Mark Word             Class MetaData Address    

      

jdk1.6之后 引入了

偏向锁:  每次获取锁和释放锁会浪费资源。很多情况下,竞争者不是由多个线程,而是由一个线程在使用。

                 

  当一个线程进来时候,找对象头查看是否是偏向锁。偏向锁的Mark Word  字段会记录一些信息 :线程id    Epoch  对象分代年龄信息  是否是偏向锁    锁标志位  

  检查锁标志位是否是偏向锁,接着会检查线程的id如果和当前线程id是一致(第一次直接进来)。第二次进来时候 接着运行同步代码块(没有锁的获取和释放)。偏向锁获取时候,没有撤销。等到有竞争时候 才有释放的机制。  场景:只有一个线程访问代码块会大大提高性能。(多个线程会降低性能)

轻量级锁:

    首先栈帧

      虚拟机栈里面存储的是一个个栈帧,每个方法执行都会存储一个栈帧。每个方法执行都会执行栈帧的进栈出栈。

      复制Mark Word 到虚拟机栈中

      竞争成功之后会将锁的标志位改成轻量级锁,然后执行同步体。  

     其他线程也是复制Mark Word到虚拟机中

      修改Mark Word,发现已经被别的线程获得了锁,所以修改不成功。然后不停的修改。直到第一个线程把锁释放了才OK

      第一个线程执行完毕,第二个获取锁之后,会升级为重量级锁(普遍意义上的synchronize),线程会阻塞。 
      

     轻量级锁用到了自旋概念。好处:多个线程可以同时

     

 

关于单例模式结合Synchronize

      单例模式与线程安全问题

         饿汉式是没有线程安全问题的

         懒汉式是有线程安全问题的

 

    缩小同步代码块的范围,只有在创建时候才有写的操作 其他的都是读的操作没有线程安全问题

     在会出现线程安全问题地方加一个synchronize,锁当前的代码 包裹起来

     这样其实还是有问题的,双重加锁,再来一个验空。

     这样视乎是一个没有问题,重排序问题。

     有可能先执行下面的 在执行上面的这样的重排序问题(没法演示)

     

   

package com.toov5.threadTest;
public class DoubleLock {

    private static volatile DoubleLock doubleLock;   //防止重排序
    
    private DoubleLock(){
        
    }
    public static DoubleLock getInstance(){  //比较饿汉模式而言的,如果在这一行加锁,效率势必会比较低  利用同步代码块
        //自旋while(true)
        if (doubleLock == null){   //判断一下  如果为null时候  这种情况下才会去创建对象
            synchronized (DoubleLock.class){ //静态的用 字节码文件    
                if (doubleLock==null) {  //上锁完了之后 可能没有创建完毕 所以要判断一下  但是 重排序时候 可能会导致 对象可能创建多次
                                         //所以加volitile关键字 (单线程是不会产生重排序问题)
                    doubleLock=new DoubleLock();
                }
            }

        }
        return doubleLock;   //不为空的话 会直接返回呀  多个线程去创建时候 才有有安全塞问题
    }
    
    
    
    public static void main(String[] args) {
         DoubleLock doubleLock1 = DoubleLock.getInstance();
         DoubleLock doubleLock2 = DoubleLock.getInstance();
         System.out.println(doubleLock1==doubleLock2);
    }
    
}

 

### Java `synchronized` 锁机制原理深入分析 #### 1. 锁的基本概念 在Java中,`synchronized` 是一种用于实现线程同步的关键字。它的主要作用是确保多个线程在访问共享资源时不会发生冲突[^3]。`synchronized` 的实现依赖于对象头中的 `Mark Word` 和 `Monitor`。 - **对象头**:每个Java对象都有一个对象头,其中包含 `Mark Word` 和类元数据地址。`Mark Word` 存储了锁的状态、线程ID等信息[^5]。 - **Monitor**:`Monitor` 是一种同步工具,它保证同一时刻只有一个线程可以进入被 `synchronized` 保护的代码块或方法。 #### 2. `synchronized` 的三种应用方式 ` synchronized` 可以应用于以下三种场景: - **修饰实例方法**:当 `synchronized` 修饰实例方法时,锁的对象是当前实例(`this`)。这意味着只有持有该实例锁的线程才能执行该方法[^1]。 - **修饰静态方法**:当 `synchronized` 修饰静态方法时,锁的对象是当前类的 `Class` 对象。所有线程在调用该静态方法时都需要获取类级别的锁[^3]。 - **修饰代码块**:通过指定一个明确的对象作为锁,`synchronized` 可以保护特定的代码块。例如: ```java synchronized (lock) { // 临界区代码 } ``` 这种方式提供了更细粒度的控制[^2]。 #### 3. 字节码指令实现 在JVM层面,`synchronized` 的实现依赖于两个字节码指令: - **`monitorenter`**:线程在进入 `synchronized` 块或方法时会执行此指令,尝试获取对象的锁。 - **`monitorexit`**:线程在退出 `synchronized` 块或方法时会执行此指令,释放对象的锁[^3]。 这两个指令的操作是原子的,确保了锁的获取和释放过程的安全性。 #### 4. 锁的状态升级 ` synchronized` 的锁状态会根据竞争情况动态升级,从偏向锁到轻量级锁再到重量级锁。以下是锁状态升级的过程: - **偏向锁**:当没有线程竞争时,JVM会假设锁只会被一个线程持有。此时,`Mark Word` 中存储的是偏向线程的ID,避免频繁的锁获取和释放操作[^5]。 - **轻量级锁**:当有线程竞争但竞争不激烈时,JVM会使用自旋的方式尝试获取锁。这种方式避免了线程切换的开销。 - **重量级锁**:当竞争非常激烈时,JVM会将线程挂起,进入操作系统级别的等待队列。 #### 5. 示例代码解析 以下是一个典型的 `synchronized` 使用示例: ```java public class SynchronizedExample { private int count = 0; public synchronized void increment() { count++; } public static void main(String[] args) throws InterruptedException { SynchronizedExample example = new SynchronizedExample(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Count: " + example.count); } } ``` 在此示例中,`increment` 方法被 `synchronized` 修饰,因此两个线程在调用该方法时会互斥执行[^1]。 #### 6. 注意事项 - `synchronized` 的锁对象必须明确且一致。如果锁对象不同,可能会导致线程之间的竞争无法正确解决[^1]。 - 在高并发场景下,`synchronized` 的性能可能不如其他锁机制(如 `ReentrantLock`),因为它缺乏一些高级特性(如公平锁、条件变量等)[^4]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值