synchronized同步锁

本文详细介绍了Java中的synchronized关键字,包括其基本概念、使用方法及注意事项。synchronized可以用作同步锁来修饰代码块、方法等,确保多线程环境下的数据一致性。

synchronized简介

synchronized是Java中的关键字,是一种同步锁。它可以修饰以下对象
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

synchronized使用注意事项

1.多个对象就有多个同步锁

synchronized获取的都是对象锁,而不是把一段代码和方法当成锁。看看下面的例子

class MyObject{
     private String passWord = "a";
     private String userName = "b";
     synchronized public void setValue(String p, String u){ 
       this.passWord = p;
        try{
            Thread.sleep(100);
        }catch(InterruptedException e){
       }
       this.userName = u;
       System.out.println(Thread.currentThread().getName() + " passWord :" + passWord);
       System.out.println(Thread.currentThread().getName() + " userName :" + userName);
    }
}

public class Thread_test {
    public static void main (String[] args){      
        MyObject obj1 = new MyObject();
        MyObject obj2 = new MyObject();

        Thread thread1 = new Thread(){
            @Override
            public void run(){
                System.out.println("线程1 开始运行");
            obj1.setValue("aa","bb");
            }
        };          
        Thread thread2 = new Thread(){
            @Override
            public void run(){
                System.out.println("线程2 开始运行");
                obj2.setValue("cc","dd");
            }
        };
        thread1.setName("线程1");
        thread1.start();
        thread2.setName("线程2");
        thread2.start();
   }
}

这里写图片描述
在上面的例子中实例化了2个MyObject对象obj1和obj2,然后别在线程1和2用调用obj1和obj2的方法setValue,可以看到程序的执行顺序和结果,他们没有受synchronized的影响,各自在自己空间运行。

2.如果线程A持有 object 对象的synchronized同步锁,这时如果线程B要调用object对象中具有synchronized修饰的方法,则需要等待线程A释放后才能执行(不管线程A和B调用的是否是同一个方法,只要是被synchronized 修饰,线程B都必须等待)。实例如下:

class MyObject{

     private String passWord = "a";
     private String userName = "b";

     synchronized public void setPassWord(String p){
         this.passWord = p;
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){

           }
           System.out.println(Thread.currentThread().getName() + " 执行完成 " + " passWord :" + passWord);
     }

     synchronized public void  setUserName(String u){
        this.userName = u;
       System.out.println(Thread.currentThread().getName() +  " 执行完成 " + "  userName :" + userName);
     }
}

public class Thread_test {
      public static void main (String[] args){
              MyObject obj1 = new MyObject();  
              Thread thread1 = new Thread(){
                  @Override
                  public void run(){
                  System.out.println("线程1 开始运行");
                  obj1.setPassWord("aa");
                  }
              };          
              Thread thread2 = new Thread(){
                  @Override
                  public void run(){
                      System.out.println("线程2 开始运行");
                      obj1.setUserName("dd");
                  }
              };

              thread1.setName("线程1");
              thread1.start();

              thread2.setName("线程2");
              thread2.start();
        }
}

这里写图片描述
可以看到在上面的例子中线程1先执行,紧接着线程2执行。但在线程1执行的方法setPassWord中休眠1秒,由于线程1获取同步锁,所以线程2要等到线程1释放了锁后,线程2才能调用setUserName函数。虽然线程1没有调用方法setUserName,但是setUserName是被synchronized修饰,所以仍然要同步。如果我们将函数setUserName前面的synchronized去掉看看执行的结果又是啥

这里写图片描述
我们看到线程2先执行完全。这说明线程A持有 object 对象的synchronized同步锁,这时如果线程B可以调用object对象中具有非synchronized修饰的方法,而不需等待线程A释放同步锁。

3.synchronized同步锁不能继承

class MyObject{
     private String passWord = "a";
     private String userName = "b";

     synchronized public void setValue(String p, String u){
             System.out.println("线程 " + Thread.currentThread().getName() +  "父类  MyObject" + " setValue 开始运行"); 
             this.passWord = p;
             this.userName = u;
             System.out.println("线程 " + Thread.currentThread().getName() + "父类  MyObject" + " passWord :" + passWord);
             System.out.println("线程 " + Thread.currentThread().getName() + "父类  MyObject" + " userName :" + userName);
        try{
            Thread.sleep(2000);
        }catch(InterruptedException e){

       }
      System.out.println("线程 " + Thread.currentThread().getName() + "父类  MyObject" + " setValue 运行结束");
    }
}

class MyObject1 extends MyObject{

     private String passWord = "a";
     private String userName = "b";

     public void setValue(String p, String u){
          System.out.println("线程 " + Thread.currentThread().getName() +  "子类  MyObject" + " setValue 开始运行");
          this.passWord = p;
          this.userName = u;

         System.out.println("线程 " + Thread.currentThread().getName() + "子类  MyObject1" + " passWord :" + passWord);
         System.out.println("线程 " + Thread.currentThread().getName() + "子类  MyObject1" + " userName :" + userName);
         super.setValue(p, u);
         System.out.println("线程 " + Thread.currentThread().getName() +  "子类  MyObject" + " setValue 运行结束");
    }
}

public class Thread_test {
    public static void main (String[] args){
        try{         
            MyObject1 obj1 = new MyObject1();
            Thread thread1 = new Thread(){
                @Override
                public void run(){
                obj1.setValue("11","22");
            }

         };
         thread1.setName("a");
         thread1.start();

             Thread.sleep(10);

         Thread thread2 = new Thread(){
             @Override
             public void run(){
                 obj1.setValue("33", "44");
             }
          };
         thread2.setName("b");
         thread2.start(); 
         }catch(InterruptedException e){
         }    
     }
}

这里写图片描述
上面的例子MyObject1继承于MyObject并重写了函数setValue,MyObject1的函数setValue没有添加同步锁,MyObject的的函数setValue添加了同步锁,我们还在MyObject1的函数setValue里面调用了父类的setValue函数。看看上面两个线程的执行结果, MyObject1的函数setValue没有添加同步锁,所以线程1,2可以各自执行,同时运行(不用等到线程1执行完成,线程2才开始执行),但是由于MyObject的的函数setValue添加了同步锁,所以MyObject1在调用父类的setValue函数时必须同步,也就是线程1运行完成了,线程2才能开始运行。

4.synchronized修饰代码块之间的同步问题

A.synchronized(this)
当一个线程访问object的一个synchronized(this)的同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码快将 被阻塞。
B. .synchronized(非this对象 x)
当一个线程访问object的一个synchronized(X)的同步代码块时,其他线程对同一个object中所有其他synchronized(X)同步代码快将 被阻塞。但是synchronized(y),synchronized(z)修饰的代码块不会被阻塞

5.synchronized修饰静态函数

A. 与修饰非静态方法的区别
synchronized关键字添加到static方法上是给Class类添加锁,而synchronized添加到非static方法上是给对象加锁
B.synchronized静态函数同步情况
当一个线程访问synchronized静态函数,其他线程可以对该类的其他synchronized静态函数访问,而不会被阻塞。

6.synchronized(object.class)修饰代码块

当一个线程访问synchronized(object.class)代码块时,其他任何线程访问该代码块时都会被阻塞。

<think> 嗯,用户需要了解synchronized同步锁的原理、使用方法和应用场景。从用户提问方式和引用资料来看,ta应该是Java开发者,可能正在处理并发问题或准备面试。 用户引用了三篇技术文章,说明ta对底层实现特别关注。第一篇提到JDK1.6的优化,第二篇讲基础用法,第三篇涉及字节码指令。需要把这些信息整合成体系化的回答。 关于原理部分,要重点说明三个层次:字节码层的monitor指令、JVM层的Monitor对象关联、JDK优化措施(偏向锁/轻量级锁这些)。用户上次问题里提到“性能超越Lock”,这点需要强调优化效果,但也要说明适用场景限制。 使用方法方面,用户给的引用2展示了基础写法,可以延伸出三种使用场景的对比:实例方法锁、静态方法锁、代码块锁。要特别注意代码块锁的灵活性,这是实际开发中最常用的。 应用场景需要典型且具体。比如转账操作要锁账户对象,配置更新要锁Class对象,订单处理要锁实例。这些例子能让用户立即理解如何落地。 用户可能隐含的性能疑惑要主动解答:虽然优化后性能提升,但长时间等待的场景还是该用Lock。这个补充能避免ta踩坑。 最后生成的问题要覆盖知识纵深(CAS对比)、横向扩展(并发工具)、实践痛点(死锁排查)。特别是死锁问题,开发中遇到时往往很头疼。 </think> 以下是关于 `synchronized` 同步锁的全面解析,涵盖原理、使用方法及应用场景: --- ### **一、实现原理** 1. **字节码指令** 通过 `monitorenter`(加锁)和 `monitorexit`(释放锁)指令实现: - 线程执行 `monitorenter` 时尝试获取对象的 **Monitor 所有权**; - 成功获取后,Monitor 进入锁定状态; - 执行 `monitorexit` 后释放 Monitor [^3]。 2. **Monitor 机制** - 每个 Java 对象关联一个 Monitor 对象(由 JVM 创建); - Monitor 包含三个关键字段: - `_owner`:持有锁的线程; - `_EntryList`:阻塞等待锁的线程队列; - `_WaitSet`:调用 `wait()` 后进入等待状态的线程队列。 3. **JDK 1.6 后的优化** - **锁升级机制**(显著提升性能): ```mermaid graph LR A[无锁] -->|首次竞争| B[偏向锁] B -->|多线程竞争| C[轻量级锁 CAS] C -->|竞争加剧| D[重量级锁 Monitor] ``` - 偏向锁:减少无竞争时的开销; - 轻量级锁:通过自旋(CAS)避免线程阻塞; - 重量级锁:竞争激烈时转为操作系统级互斥锁 [^1]。 --- ### **二、使用方法** #### **1. 步代码块** ```java public void func() { synchronized (lockObject) { // 锁对象可以是任意实例或 Class 对象 // 临界区代码 } } ``` - **锁对象选择**: - 实例对象锁:`synchronized(this)` 或 `synchronized(new Object())`; - 类对象锁:`synchronized(MyClass.class)`。 #### **2. 步方法** ```java // 实例方法锁:锁住当前对象实例 public synchronized void instanceMethod() { ... } // 静态方法锁:锁住类的 Class 对象 public static synchronized void staticMethod() { ... } ``` --- ### **三、应用场景** 1. **实例数据保护** ```java public class BankAccount { private double balance; public synchronized void transfer(double amount) { balance += amount; // 保证转账操作原子性 } } ``` 2. **静态资源共享** ```java public class ConfigLoader { private static Map<String, String> configCache; public static synchronized void reloadConfig() { // 保证全局配置加载线程安全 } } ``` 3. **双重检查锁定(DCL)** ```java public class Singleton { private volatile static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` --- ### **四、注意事项** 1. **避免死锁**: 确保多个线程的锁获取顺序一致(如按对象哈希值排序)。 2. **锁粒度优化**: - 高并发场景拆分锁:如 `ConcurrentHashMap` 分段锁; - 读写分离场景:优先使用 `ReentrantReadWriteLock`。 3. **性能权衡**: - 低竞争时:`synchronized` 因 JIT 优化性能接近 Lock; - 高竞争时:`ReentrantLock` 的 `tryLock()` 和公平锁策略更灵活。 > **💎 核心总结**:`synchronized` 是 JVM 层实现的互斥锁,通过 Monitor 和锁升级机制平衡安全性与性能,适用于大部分线程步场景。但在需要超时锁、可中断锁或公平锁时,需改用 `Lock` 接口实现 [^1][^2]。 --- **相关问题** 1. `synchronized` 和 `ReentrantLock` 在哪些场景下性能差异显著? 2. 如何通过 Jstack 诊断 `synchronized` 导致的线程阻塞? 3. 偏向锁的撤销流程在 JVM 中如何触发? 4. 为什么双重检查锁定需要 `volatile` 关键字?
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值