Java代码块加锁
引言
在多线程编程中,为了保证多个线程的协调和数据的一致性,我们需要使用锁机制来控制对共享资源的访问。Java中提供了synchronized关键字和Lock接口来实现线程的同步。本文将重点介绍Java中的代码块加锁,探讨其原理和使用方法。
代码块加锁
Java中的代码块加锁是指使用synchronized关键字对代码块进行加锁,以实现对共享资源的互斥访问。通过加锁,我们可以确保在同一时刻只有一个线程可以进入被锁定的代码块,从而避免了多线程竞争导致的数据不一致性问题。
在Java中,可以对任意对象进行加锁,而不仅限于类的实例对象。当一个线程进入被加锁的代码块时,它会尝试获取对象的锁。如果锁已经被其他线程获取,则该线程进入阻塞状态,直到锁被释放。
代码块加锁的语法如下所示:
synchronized (object) {
// 需要同步的代码块
}
其中,object
是需要加锁的对象。当多个线程同时执行到这段代码时,只有一个线程能够获取到object
的锁,并执行代码块中的代码,其他线程则等待。
代码块加锁的应用非常广泛,可以用于保护对共享资源的访问,例如对临界区的操作、对共享变量的读写等。下面我们将通过一个简单的示例来展示代码块加锁的用法和效果。
示例
假设有一个银行账户类BankAccount
,其中包含了一个balance
变量表示账户余额。我们希望多个线程能够并发地对账户进行存款和取款操作,但要保证操作的原子性和一致性。
首先,我们定义BankAccount
类如下:
public class BankAccount {
private double balance;
public BankAccount(double balance) {
this.balance = balance;
}
public double getBalance() {
return balance;
}
public synchronized void deposit(double amount) {
balance += amount;
}
public synchronized void withdraw(double amount) {
balance -= amount;
}
}
在BankAccount
类中,我们使用synchronized
关键字对deposit
和withdraw
方法进行了同步。这样,当多个线程同时调用这两个方法时,只有一个线程能够获取BankAccount
对象的锁,并执行相应的操作。
接下来,我们创建两个线程进行并发的存款和取款操作:
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount(1000);
Thread depositThread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
account.deposit(10);
}
});
Thread withdrawThread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
account.withdraw(10);
}
});
depositThread.start();
withdrawThread.start();
try {
depositThread.join();
withdrawThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final balance: " + account.getBalance());
}
}
在Main
类中,我们创建了一个BankAccount
对象,并使用两个线程分别进行存款和取款操作。通过synchronized
关键字,我们确保了这两个操作的原子性和一致性。
最后,我们在Main
类中输出账户的最终余额。由于存款和取款操作是并发进行的,如果没有使用锁机制,可能会导致最终余额不正确。但是,由于我们使用了代码块加锁,所以最终输出的余额是正确的。
序列图
下面是一个使用mermaid语法标识的序列图,展示了代码块加锁的工作原理:
sequenceDiagram
participant