我们知道多线程优点很多,但是也有缺点,比如:
所以本讲,为了解决线程不安全问题,将讲解Java另一个关键字Synchronized,Synchronized是Java语言提供的一种内置的线程同步机制,它可以用来解决多线程并发访问共享资源时的线程安全问题。Synchronized可以用于同步方法和同步代码块,确保同一时间只有一个线程可以访问共享资源。
在Java多线程编程中,Synchronized关键字是一种重要的同步机制,用于实现线程间的互斥和同步。它提供了一种在多个线程之间共享资源的安全保障,确保同一时刻只有一个线程可以访问被保护的代码块或方法。Synchronized的使用可以避免多线程并发导致的数据竞争和不一致
内部机制,Synchronized通过对象头中的标记位来实现线程间的互斥和同步。当一个线程进入synchronized修饰的代码块或方法时,它会尝试获取对应的锁。如果该锁被其他线程持有,则该线程进入等待状态,直到持有锁的线程释放锁。一旦该线程获得锁,其他尝试获取该锁的线程将会被阻塞,直到该线程释放锁。
在Java中,Synchronized使用了可重入的锁机制。这意味着一个线程可以多次获得同一个锁,只要在每次获得锁后都释放该锁,就不会发生死锁。另外,如果一个线程在等待获取锁的过程中另一个线程释放了该锁,那么该线程将会被唤醒并获取到该锁。
在Java中,Synchronized提供了等待和通知机制。当一个线程进入等待状态时,它会释放所持有的锁,其他等待的线程有机会获取该锁并继续执行。当持有锁的线程释放锁时,它会通知等待队列中的某个线程获取该锁并继续执行。这种机制可以有效地协调多个线程之间的协同工作。
使用Synchronized修饰方法时,整个方法都被标记为同步方法。在调用该方法时,只有一个线程能够进入该方法执行,其他线程需要等待当前线程执行完毕后才能继续执行。
public synchronized void synchronizedMethod() {
// 同步方法的代码
}
使用Synchronized关键字加上括号,后面跟上需要同步的对象的引用,可以用来标记某个代码块为同步代码块。在同一时刻只能有一个线程执行该代码块,其他线程需要等待当前线程执行完毕后才能继续执行。
public void someMethod() {
synchronized (this) {
// 同步代码块的代码
}
}
使用Synchronized修饰静态方法时,多个线程可以同时访问该静态方法,但是每次只能有一个线程执行该静态方法。其他线程需要等待当前线程执行完毕后才能继续执行
public static synchronized void synchronizedStaticMethod() {
// 同步静态方法的代码
}
我们模拟是银行账户的转账问题。假设有两个账户,A和B,初始金额分别为A: 1000, B: 500。现在需要编写一个多线程程序,实现从A向B转移100的操作。
public class BankAccount {
private int balance;
public BankAccount(int balance) {
this.balance = balance;
}
public void withdraw(int amount) {
if (amount > balance) {
System.out.println("Insufficient balance");
} else {
balance -= amount;
System.out.println("Withdrawal successful");
}
}
public void deposit(int amount) {
balance += amount;
System.out.println("Deposit successful");
}
public int getBalance() {
return balance;
}
}
public class TransferThread extends Thread {
private BankAccount fromAccount;
private BankAccount toAccount;
private int amount;
public TransferThread(BankAccount fromAccount, BankAccount toAccount, int amount) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
}
@Override
public void run() {
if (fromAccount.getBalance() >= amount) {
fromAccount.withdraw(amount);
toAccount.deposit(amount);
System.out.println("Transfer successful");
} else {
System.out.println("Insufficient balance in account " + fromAccount);
}
}
}
在上述代码中,两个线程同时对同一个账户进行操作,一个线程从A账户取款100,另一个线程向B账户存款100。如果没有适当的同步措施,就可能出现线程安全问题。例如,当一个线程在执行withdraw()方法时,另一个线程同时执行deposit()方法,导致B账户的存款金额被重复计算,从而导致B账户的实际金额多于预期金额。
运行结果:
public class BankAccount {
private int balance;
public BankAccount(int balance) {
this.balance = balance;
}
public synchronized void withdraw(int amount) {
if (amount > balance) {
System.out.println("Insufficient balance");
} else {
balance -= amount;
System.out.println("Withdrawal successful");
}
}
public synchronized void deposit(int amount) {
balance += amount;
System.out.println("Deposit successful");
}
public synchronized int getBalance() {
return balance;
}
}
public class TransferThread extends Thread {
private BankAccount fromAccount;
private BankAccount toAccount;
private int amount;
public TransferThread(BankAccount fromAccount, BankAccount toAccount, int amount) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
}
@Override
public void run() {
synchronized (fromAccount) {
if (fromAccount.getBalance() >= amount) {
fromAccount.withdraw(amount);
toAccount.deposit(amount);
System.out.println("Transfer successful");
} else {
System.out.println("Insufficient balance in account " + fromAccount);
}
}
}
}
在BankAccount类中,将withdraw()、deposit()和getBalance()方法都标记为synchronized,确保同一时刻只有一个线程可以访问这些方法。在TransferThread类中,使用synchronized块来确保在执行转账操作时只有一个线程可以访问fromAccount对象。这样可以避免多个线程同时对同一个账户进行操作,确保线程安全。
运行结果:
Synchronized是Java多线程编程中的关键,类似多线程的一把锁,它提供了一种简单而有效的机制来确保线程安全和避免竞争条件。通过使用Synchronized关键字,我们可以实现方法级别的同步和代码块级别的同步,以保护共享资源并确保同一时刻只有一个线程可以访问这些资源。
不过在使用Synchronized关键字修饰代码块时,需要注意以下几点:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。