MST
星途 面试题库

面试题:Java中synchronized关键字的工作原理及应用场景

请详细阐述Java中synchronized关键字是如何实现线程同步的,包括其在对象锁、类锁方面的原理。并举例说明在实际开发中,哪些场景适合使用synchronized关键字来保证线程安全。
13.7万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. synchronized实现线程同步原理

对象锁原理

  • 监视器锁(Monitor):在Java中,每个对象都有一个监视器锁(Monitor)与之关联。当线程执行到synchronized修饰的实例方法或者以对象为锁的同步代码块时,线程需要先获取该对象的监视器锁。
  • 获取与释放:如果该对象的监视器锁没有被其他线程持有,那么当前线程可以获取到该锁,进入同步代码块或方法执行;当线程执行完同步代码块或方法,或者在代码块中抛出异常时,线程会自动释放该对象的监视器锁,以便其他线程可以竞争获取。

例如,以下代码中this就是对象锁:

public class SynchronizedExample {
    public synchronized void synchronizedMethod() {
        // 同步代码逻辑
    }

    public void synchronizedBlock() {
        synchronized (this) {
            // 同步代码逻辑
        }
    }
}

类锁原理

  • 类对象的监视器锁:类锁实际上是针对类的Class对象的锁。因为Class对象在JVM中是唯一的,所以类锁也只有一把。当线程执行synchronized修饰的静态方法或者以类的Class对象为锁的同步代码块时,线程需要获取该类的Class对象的监视器锁。
  • 同样的获取与释放机制:获取和释放机制与对象锁类似。当线程获取到类锁后,才能进入同步代码块或方法执行,执行完毕后释放锁。

例如:

public class SynchronizedClassExample {
    public static synchronized void synchronizedStaticMethod() {
        // 同步代码逻辑
    }

    public static void synchronizedStaticBlock() {
        synchronized (SynchronizedClassExample.class) {
            // 同步代码逻辑
        }
    }
}

2. 适合使用synchronized关键字的场景

多线程访问共享资源

当多个线程需要访问并修改共享的资源(如共享变量、文件等)时,为了保证数据的一致性和线程安全,可以使用synchronized关键字。例如,银行账户类中多个线程对账户余额进行操作:

public class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    public synchronized void deposit(double amount) {
        balance += amount;
    }

    public synchronized void withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
        }
    }

    public double getBalance() {
        return balance;
    }
}

避免数据竞争

在一些需要保证操作原子性的场景下,如计数器的自增操作。如果不使用同步机制,多个线程同时对计数器进行自增可能会导致结果不准确。

public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

控制并发访问频率

在一些场景下,可能需要控制对某些资源的并发访问频率,例如数据库连接池的获取连接操作。通过synchronized关键字,可以保证同一时间只有一个线程能够获取连接,避免过多线程同时获取连接导致资源耗尽。

public class ConnectionPool {
    private int availableConnections;

    public ConnectionPool(int initialConnections) {
        this.availableConnections = initialConnections;
    }

    public synchronized Connection getConnection() {
        while (availableConnections <= 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        availableConnections--;
        // 返回连接对象
        return null;
    }

    public synchronized void releaseConnection(Connection connection) {
        availableConnections++;
        notify();
    }
}