Synchronized的介绍及其用法
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的观点:
-
锁粒度较大:使用synchronized关键字时,通常需要锁定整个方法或代码块,这可能会降低并发性能。对于细粒度的锁需求,可以考虑使用更灵活的锁机制,例如ReentrantLock等。
-
可能导致死锁:如果对于多个资源或锁的请求没有正确处理,就有可能导致死锁情况的发生。避免死锁需要谨慎地设计和管理锁的使用。
-
可能导致性能问题:由于synchronized是独占锁,可能会导致线程竞争和等待的情况,进而影响程序的执行效率。在高并发场景下,可能需要考虑使用更高级的并发工具,如ConcurrentHashMap或并发集合类来提高性能。
-
更多选择:随着Java并发编程领域的不断发展,出现了许多更高级的并发工具和框架,例如AQS(AbstractQueuedSynchronizer)、java.util.concurrent包中的工具类等,这些工具提供了更多的灵活性和功能,使得开发者能够更好地处理并发编程中的各种问题。