Java使用线程同步解决线程安全问题详解
概述
Java中多线程带来的好处是同时可以执行多个任务,但是线程之间共享同一个全局变量或对象可能会出现线程安全问题。线程安全问题的解决最主要的方法是使用锁机制,也就是线程同步来保证同一时刻只有一个线程能够访问共享变量或对象。
线程同步可以通过synchronized关键字来实现,synchronized关键字可以保证同一时刻只有一个线程执行某个方法或代码段。
示例
示例1:使用synchronized方法
下面是使用synchronized方法来解决线程安全问题的示例。在该示例中,有两个线程同时访问一个银行账户的balance变量,这里的balance变量是一个共享变量。
public class BankAccount {
private int balance = 100;
public synchronized void withdraw(int amount) throws InterruptedException {
if (balance >= amount) {
Thread.sleep(1000);
balance -= amount;
System.out.println("Withdraw " + amount + ", balance is " + balance);
} else {
System.out.println("Not enough balance");
}
}
public synchronized void deposit(int amount) throws InterruptedException {
Thread.sleep(500);
balance += amount;
System.out.println("Deposit " + amount + ", balance is " + balance);
}
}
public class User implements Runnable {
private BankAccount bankAccount;
public User(BankAccount bankAccount) {
this.bankAccount = bankAccount;
}
public void run() {
try {
bankAccount.withdraw(80);
Thread.sleep(500);
bankAccount.deposit(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
BankAccount bankAccount = new BankAccount();
User user1 = new User(bankAccount);
User user2 = new User(bankAccount);
Thread thread1 = new Thread(user1);
Thread thread2 = new Thread(user2);
thread1.start();
thread2.start();
}
}
在上面的代码中,BankAccount类中的withdraw和deposit方法都使用了synchronized关键字来保证同一时刻只有一个线程能够执行该方法。因此不会出现多个线程同时对balance变量进行读写的情况,从而保证了线程安全性。该示例中的输出结果如下:
Withdraw 80, balance is 20
Deposit 50, balance is 70
Withdraw 80, balance is -10
Not enough balance
示例2:使用synchronized代码块
除了使用synchronized方法外,还可以使用synchronized代码块来解决线程安全问题。在下面的示例中,我们可以看到在BankAccount类的transfer方法中,我们使用了synchronized代码块来同步对from和to两个对象的读和写操作。
public class BankAccount {
private int balance = 100;
public void transfer(BankAccount from, BankAccount to, int amount) throws InterruptedException {
synchronized (from) {
synchronized (to) {
if (from.balance >= amount) {
Thread.sleep(1000);
from.balance -= amount;
to.balance += amount;
System.out.println("Transfer " + amount);
}
}
}
}
}
public class User implements Runnable {
private BankAccount from;
private BankAccount to;
public User(BankAccount from, BankAccount to) {
this.from = from;
this.to = to;
}
public void run() {
try {
from.transfer(from, to, 20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
BankAccount account1 = new BankAccount();
BankAccount account2 = new BankAccount();
account1.transfer(account1, account2, 20);
Thread thread1 = new Thread(new User(account1, account2));
Thread thread2 = new Thread(new User(account2, account1));
thread1.start();
thread2.start();
}
}
在上面的代码中,当一个线程获取了from对象的锁后,它就可以执行方法体中的代码,同时其他线程只能等待该线程释放from对象的锁才能获取锁进入临界区。同样地,当一个线程获取了to对象的锁后,其他线程也必须等待该线程释放to对象的锁才能获取锁进入临界区。这样通过锁的机制控制了线程执行过程中对from和to对象共享变量的读与写,从而避免了线程安全问题。
结论
以上是关于Java使用线程同步解决线程安全问题的详解攻略,可以发现使用synchronized关键字是最常用的方法,能够保证线程安全性,同时也可以使用synchronized代码块来对共享变量进行同步控制。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java使用线程同步解决线程安全问题详解 - Python技术站