多线程运行问题
- 各个线程是通过竞争CPU时间而获得运行机会的
- 各线程什么时候得到CPU时间,占用多久,是不可预测的一个正在运行着
- 线程在什么地方被暂停是不确定的
编写银行存、取款代码:
package java_thread;
public class BankThread {
private String account; //账号
private int balance; //账户余额
public BankThread(String account, int balance){
this.account = account;
this.balance = balance;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public String toString(){
return "Bank [账号:" + account + ", 余额:" + balance + "]";
}
//存款
public void saveAccount(){
//获取当前的账号余额
int balance = getBalance();
//修改余额,存100
balance += 100;
//修改账户余额
setBalance(balance);
//输出存款后的账户余额
System.out.println("存款后的账户余额为:" + balance);
}
//取款
public void drawAccount(){
//获得当前的账户余额
int balance = getBalance();
//修改余额,取200
balance = balance - 200;
//修改账户余额
setBalance(balance);
System.out.println("取款后的账户余额:" + balance);
}
}
存款类,实现Runnable接口:
package java_thread;
//存款
public class SaveAccount implements Runnable {
BankThread bank;
public SaveAccount(BankThread bank){
this.bank = bank;
}
@Override
public void run() {
bank.saveAccount();
}
}
取款类,实现Runnable接口:
package java_thread;
//取款
public class DrawAccount implements Runnable {
BankThread bank;
public DrawAccount(BankThread bank){
this.bank = bank;
}
@Override
public void run() {
bank.drawAccount();
}
}
进行测试:
package java_thread;
public class BankTest {
public static void main(String[] args) {
//创建账户,给定余额为1000
BankThread bank = new BankThread("s01", 1000);
//创建线程对象
SaveAccount save = new SaveAccount(bank);
DrawAccount draw = new DrawAccount(bank);
Thread sa = new Thread(save);
Thread dr = new Thread(draw);
sa.start();
dr.start();
try {
dr.join();
sa.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bank);
}
}
输出:
存款后的账户余额为:1100
取款后的账户余额:900
Bank [账号:s01, 余额:900]
结果看似好像没有什么问题。
若在存取款方法中添加休眠状态:
//存款
public void saveAccount(){
//获取当前的账号余额
int balance = getBalance();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改余额,存100
balance += 100;
//修改账户余额
setBalance(balance);
//输出存款后的账户余额
System.out.println("存款后的账户余额为:" + balance);
}
//取款
public void drawAccount(){
//获得当前的账户余额
int balance = getBalance();
//修改余额,取200
balance = balance - 200;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改账户余额
setBalance(balance);
System.out.println("取款后的账户余额:" + balance);
}
再次输出:
存款后的账户余额为:1100
取款后的账户余额:800
Bank [账号:s01, 余额:800]
结果和之前不一样了,那是因为,在执行存款方法的存款操作之前进入了休眠,此时取款方法获取了cpu的使用权,先执行了取款操作(1000-200=800),但取款后没来得及更新余额(此时余额还是起初设定的1000)又进入休眠状态,这时存款的休眠时间就到了,又接着执行存款操作(1000+100=1000),所以造成了上面的结果。
若这种情况真正发生在银行中,会造成很严重的后果,存款和取款的线程都对BankThread类的对象进行操作,这种机制称为线程的同步或线程的互斥。
- 为了保证在存款或取款的时候,不允许其他线程对帐户余额进行操作
- 需要将Bank对象进行锁定
- 使用关键字synchronized实现
synchronized同步关键字用在
- 成员方法
public synchronized void saveAccount(){}
- 静态方法
public static synchronized void saveAccount(){}
- 语句块
synchronized (obj){……}
使用该关键字的方法执行完前,其他线程不能够打断它。
使用synchronized对代码进行修改:
//存款
public synchronized void saveAccount(){
//获取当前的账号余额
int balance = getBalance();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改余额,存100
balance += 100;
//修改账户余额
setBalance(balance);
//输出存款后的账户余额
System.out.println("存款后的账户余额为:" + balance);
}
//取款
public void drawAccount(){
synchronized(this){
//获得当前的账户余额
int balance = getBalance();
//修改余额,取200
balance = balance - 200;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改账户余额
setBalance(balance);
System.out.println("取款后的账户余额:" + balance);
}
}
输出:
存款后的账户余额为:1100
取款后的账户余额:900
Bank [账号:s01, 余额:900]