synchronized锁了什么

本文深入解析Java中的synchronized关键字,探讨其作为同步锁的作用机制,包括对象锁与类锁的概念,以及底层实现原理。通过示例代码展示synchronized如何确保线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

synchronized翻译为中文的意思是同步的,它是Java中处理线程安全问题常用的关键字。也有人称其为同步锁。既然是锁,其必然有锁的东西,下面先会简单介绍一下synchronized,再通过一个示例代码展示synchronized锁了什么。(这里先提前透露答案synchronized锁的是代码)

介绍

定义

synchronized提供的同步机制确保了同一个时刻,被修饰的代码块或方法只会有一个线程执行

用法

synchronized可以修饰方法和代码块:

  1. 修饰普通方法
  2. 修饰静态方法
  3. 修饰代码块

根据修饰情况,分为对象锁和类锁:

  1. 对象锁:
    • 普通方法(等价于代码块修饰this
    • 代码块修饰的是是类的一个对象
  2. 类锁
    • 类方法(等价于代码块修饰当前类Class对象)
    • 代码块修饰的是是类Class对象

原理

synchronized底层原理是使用了对象持有的监视器monitor)。但是同步代码块和同步方法的原理存在一点差异:

  • 同步代码块是使用monitorentermonitorexit指令实现的
  • 同步方法是由方法调用指令读取运行时常量池中方法的ACC_SYNCHRONIZED标识隐式实现,实际上还是调用了monitorentermonitorexit指令

测试示例

计数器

一个特殊的计数器,自增方法increase()synchronized修饰,而获取当前值方法getCurrent()则没有被synchronized修饰。

/**
 * 计数器
 * @author RJH
 * create at 2019-03-13
 */
public class Counter {
    /**
     * 全局对象,总数
     */
    private static int i = 0;

    /**
     * 自增
     * @return
     */
    public synchronized int increase() {
        try {
            //使用休眠让结果更明显
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return ++i;
    }

    /**
     * 获取当前值
     * @return
     */
    public int getCurrent() {
        return i;
    }
}

测试代码

使用自增线程和获取当前值的线程来验证synchronized锁的是代码,而不是全局变量

/**
 * synchronized锁了什么
 * @author RJH
 * create at 2019-03-02
 */
public class LockWhatTest {

    public static void main(String[] args) {
        Counter counter =new Counter();
        IncreaseThread increaseThread1=new IncreaseThread(counter);
        IncreaseThread increaseThread2=new IncreaseThread(counter);
        GetThread getThread=new GetThread(counter);
        increaseThread1.start();
        increaseThread2.start();
        //直到increaseThread的线程启动才执行下一步
        while (increaseThread1.getState().compareTo(Thread.State.NEW)==0 && increaseThread1.getState().compareTo(Thread.State.NEW)==0){

        }
        getThread.start();
    }

    /**
     * 自增线程
     */
    static class IncreaseThread extends Thread{

        private Counter counter;

        public IncreaseThread(Counter counter) {
            this.counter = counter;
        }

        @Override
        public void run() {
            System.out.println("After increase:" + counter.increase()+",trigger time:"+System.currentTimeMillis());
        }
    }

    /**
     * 获取当前值的线程
     */
    static class GetThread extends Thread{

        private Counter counter;

        public GetThread(Counter counter) {
            this.counter = counter;
        }

        @Override
        public void run() {
            System.out.println("Current:"+ counter.getCurrent()+",trigger time:"+System.currentTimeMillis());
        }
    }

}

执行结果

Current:0,trigger time:1552487003845
After increase:1,trigger time:1552487008846
After increase:2,trigger time:1552487013848

结果分析

从测试结果可以得知

  1. 在两个自增线程启动后,获取当前值的线程才启动,但是获取当前值的线程是先被执行完成了。
  2. 根据自增线程执行完成的时间戳间隔可以得知,两个自增线程是依次执行的。

从而可以证明

  1. synchronized并不是锁定方法内访问的变量
  2. synchronized锁定的是同一个监视器对象监视的代码
<think>好的,我现在需要回答用户关于Java中synchronized关键字的作用和使用场景的问题。首先,我应该先回顾一下自己对这个关键字的理解,确保信息的准确性。 synchronized是Java中用于实现线程同步的重要关键字。我记得它的主要作用是确保多个线程在访问共享资源时的安全性,防止数据不一致的问题。根据引用[3],synchronized提供了原子性、可见性和有序性的保证。原子性意味着操作不可中断,要么全部完成,要么都不执行;可见性确保一个线程修改共享变量后,其他线程能立即看到变化;有序性则防止指令重排序带来的问题。 接下来是使用场景。用户可能想知道在什么情况下需要使用synchronized。根据引用[2],当需要维护多个变量之间的一致性时,必须使用synchronized。例如,银行转账操作中,两个账户的余额需要同时更新,这时候必须用来保证操作的原子性。引用[4]也提到,如果不合理使用synchronized可能导致性能问题或死,所以需要谨慎选择使用场景。 关于具体用法,引用[3]提到synchronized可以修饰代码块、实例方法和静态方法。修饰代码块时需要指定对象,而实例方法默认用当前实例作为,静态方法则用类的Class对象作为。例如,在单例模式中,双重检查定需要正确使用synchronized来保证线程安全。 我还需要考虑是否需要举例说明,比如代码示例。例如,展示一个使用synchronized代码块保护共享变量的例子,或者修饰方法实现同步。但根据用户的问题,可能不需要详细代码,但适当举例有助于理解。 另外,需要注意用户提供的引用中有没有需要特别注意的地方。引用[1]虽然链接是关于synchronized的原理分析,但用户的问题更关注作用和使用场景,所以可能需要重点参考引用[2]、[3]、[4]的内容。 最后,生成相关问题时要围绕synchronized的关键作用、优化方法、与其他机制的区别等,确保问题有助于用户进一步学习。例如,如何避免死,或者synchronized和ReentrantLock的对比。 总结一下,回答的结构应该分为作用、使用场景、用法及注意事项,每个部分结合引用内容,确保准确性和权威性。同时,语言要简洁明了,符合中文表达习惯,避免技术术语过于复杂。</think>### synchronized关键字的作用及使用场景 #### 一、作用 1. **原子性** 通过机制,确保被修饰的代码块或方法在同一时刻只能被一个线程执行,从而保证多步操作不可分割。例如,银行转账操作中更新转出账户和转入账户的余额必须作为整体完成[^3]。 2. **可见性** 线程释放时,会将共享变量的修改刷新到主内存;获取时,会从主内存重新加载最新值。这保证了多线程间的数据一致性[^3]。 3. **有序性** 通过限制指令重排序,确保代码执行顺序符合预期。 #### 二、使用场景 1. **多线程共享资源访问** 当多个线程需要读写同一变量(如计数器、缓存等)时,必须使用`synchronized`避免数据竞争。 **示例**:电商系统中库存扣减操作。 2. **需要保证多变量一致性** 若逻辑涉及多个共享变量的修改(如转账需同时修改两个账户余额),必须通过确保操作的原子性[^2]。 3. **单例模式双重检查定** 在延迟初始化场景中,使用`synchronized`避免多线程重复创建实例。 **示例**: ```java public class Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` #### 三、用法 1. **修饰代码块** 指定对象(如`this`或类实例),控制同步范围: ```java public void method() { synchronized (this) { // 操作共享资源 } } ``` 2. **修饰实例方法** 对象为当前实例,适用于对象级别的同步: ```java public synchronized void increment() { count++; } ``` 3. **修饰静态方法** 对象为类的`Class`对象,适用于类级别的全局同步: ```java public static synchronized void log(String message) { // 写入日志文件 } ``` #### 四、注意事项 1. **性能影响** 的粒度过大会导致线程阻塞,降低并发效率。建议尽量缩小同步代码块的范围[^4]。 2. **死风险** 避免嵌套定多个资源,或按固定顺序获取。例如: ```java // 错误示例:线程A定obj1后请求obj2,线程B定obj2后请求obj1 synchronized (obj1) { synchronized (obj2) { /* ... */ } } ``` 3. **对象选择** 使用与共享资源直接关联的对象作为,避免误用无关对象导致同步失效。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值