由于同一个进程的多个线程共享存储空间,在带来方便的同时也产生了一些访问冲突的问题。如果两个线程同时访问一个共享变量(如例子中的余额balance),会造成最终结果不符合实际需求的情况。
Java中引入了“对象互斥锁”的概念(又称为监视器)来实现不同线程对共享数据操作的同步。“对象互斥锁”不允许多个线程对象同时访问同一个条件变量,即同一时刻最多只有一个线程对象访问共享数据。
我们通过private修饰变量,让变量只能被方法访问,所以针对方法提出一套机制——synchronized关键字,它包含两种用法:synchronized修饰方法和synchronized修饰程序快。
一、synchronized修饰方法
格式:public synchronized void withdrawal(double amount){}
该机制保证了同一时刻内,一个类实例中所有声明synchronized的方法最多只有一个处于可执行的状态,从而避免了类成员变量的访问冲突。
所以synchronized只用修饰会访问共享变量的方法即可,而常规方法不用,synchronized修饰方法的案例请看“线程的通信中第一个例子”。
二、synchronzed修饰程序块
格式:synchronized(syncObject){
//允许访问控制的代码
}
synchronized程序块是对一个类实例对象进行上锁,保证该类实例对象同一时刻只能被一个线程访问。代码如下:
public class BankDriver {
public static void main(String[] args) {
//初始化一个银行账号,余额为2000
Bank bank = new Bank("001", 2000);
//开启两个Operation线程对象对bank账号进行取款操作
for (int i=0;i<2;i++){
new Operation(i+"#",bank, (double) 1200,1).start();
}
}
}
public class Operation extends Thread {
//定义银行账户对象、操作金额变量、存取款flag(1代表取款,0代表存款)
Bank bank;
double amount;
int flag;
public Operation(String name) {
super(name);
}
public Operation(String name,Bank bank,Double amouot,int flag){
super(name);
this.bank = bank;
this.amount = amouot;
this.flag = flag;
}
@Override
public void run() {
//将bank设置为上锁对象,通过flag判断是存款还是取款执行对应逻辑
synchronized(bank){
if (flag == 1) {
if (bank.getBalance() >= amount){
System.out.println("****************************");
bank.setBalance(bank.getBalance() - amount);
System.out.println("已取出" + amount + "元,\n目前余额为:" + bank.getBalance() + "元。");
System.out.println("****************************");
}else {
System.out.println("余额不足!");
}
} else {
bank.setBalance(bank.getBalance() + amount);
System.out.println("*********************************");
System.out.println("已存入" + amount + "元,\n目前余额为:" + bank.getBalance() + "元。");
System.out.println("*********************************");
}
}
}
}
public class Bank {
//账户id和余额
private String bankID;
private double balance;
public Bank() {
}
public Bank(String bankID, double balance) {
this.bankID = bankID;
this.balance = balance;
}
public String getBankID() {
return bankID;
}
public void setBankID(String bankID) {
this.bankID = bankID;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
结果如下:如果没有设置线程同步机制,可能会造成余额为负。