Synchronized的理解及其用法

发布时间 2023-08-30 15:45:20作者: ashet

synchronized 关键字用于实现对象级别的同步,它可以保证多个线程在访问某个对象时的互斥性,避免并发访问导致的数据竞争和不一致

public class BankAccount {
    private BigDecimal balance;

    public BankAccount(String initialValue) {
        this.balance = new BigDecimal(initialValue);
    }

    /**
     * 存款
     */
    public synchronized void deposit(String amount) {
        BigDecimal depositAmount = new BigDecimal(amount);
        balance = balance.add(depositAmount);
    }

    /**
     * 取款
     */
    public synchronized void withdraw(String amount) {
        BigDecimal withdrawAmount = new BigDecimal(amount);
        if (balance.compareTo(withdrawAmount) >= 0) {
            balance = balance.subtract(withdrawAmount);
        } else {
            throw new ServiceException("余额不足");
        }
    }

    public synchronized String getBalance() {
        return balance.toString();
    }
}
    @Test
    void synchronizedMethod() {
        Assertions.assertDoesNotThrow(() -> {
            // 初始化一个100元余额的银行账户
            BankAccount bankAccount = new BankAccount("100");

            // 创建多个线程执行存款、取款
            Thread t1 = new Thread(() -> {
                for (int i = 0; i < 2; i++) {
                    // 存款
                    bankAccount.deposit("50");
                }
            });

            Thread t2 = new Thread(() -> {
                for (int i = 0; i < 2; i++) {
                    // 取款
                    bankAccount.withdraw("100");
                }
            });

            t1.start();
            t2.start();

            /*
                join方法会使得主线程阻塞,直到t1、t2线程执行完毕
                若注释此段代码,那么最后输出的Balance则不一定是0
                多线程编程中,当存在共享数据时,正确的同步机制是至关重要的,以保证数据的可见性和一致性
             */
            try {
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                log.error("InterruptedException, Cause by", e);
            }

            log.info("Balance: " + bankAccount.getBalance());
        });
    }

以下是一些关于synchronized的观点:

  1. 锁粒度较大:使用synchronized关键字时,通常需要锁定整个方法或代码块,这可能会降低并发性能。对于细粒度的锁需求,可以考虑使用更灵活的锁机制,例如ReentrantLock等。

  2. 可能导致死锁:如果对于多个资源或锁的请求没有正确处理,就有可能导致死锁情况的发生。避免死锁需要谨慎地设计和管理锁的使用。

  3. 可能导致性能问题:由于synchronized是独占锁,可能会导致线程竞争和等待的情况,进而影响程序的执行效率。在高并发场景下,可能需要考虑使用更高级的并发工具,如ConcurrentHashMap或并发集合类来提高性能。

  4. 更多选择:随着Java并发编程领域的不断发展,出现了许多更高级的并发工具和框架,例如AQS(AbstractQueuedSynchronizer)、java.util.concurrent包中的工具类等,这些工具提供了更多的灵活性和功能,使得开发者能够更好地处理并发编程中的各种问题。