最近在复习Java基础,看到多线程这块顺便写写多线程的协调控制程序。
需求:假设系统中有两个线程分别代表取款者和存款者,现在系统的要求是存款者和取款者不断的重复存、取款操作,
并且要求每当有存款者将钱存入指定账户中时,取款者就立即取出这笔钱,即不允许存款者连续两次存钱,也不允许
取款者两次取钱。
下面代码实现:
1.首先是账户Account类;
package com.xjtu.cruise.soft.thread;
public class Account {
/**
* @author Cruise
* @param args
*/
private String accountNo;
//标识账户是否还有存款的
private boolean flag = false;
private double balance;
public Account(){}
public Account(String accountNo, double balance) {
this.accountNo = accountNo;
this.balance = balance;
}
public double getBalance(){
return balance;
}
public synchronized void draw(double drawAmount) {
try {
if (!flag) { //如果账户没钱
wait(); //阻塞当前的方法,并释放同步锁(这里就是this,也就是调用此方法的对象)
} else {
System.out.println("线程:" + Thread.currentThread().getName()
+ "取款" + drawAmount);
balance -= drawAmount;
System.out.println("账户余额:" + balance);
flag = false;
notifyAll(); //唤醒等待此同步锁的所有线程
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void deposit(double depositAmount) {
try{
if(flag){ //如果账户有存入的钱
wait();
}else{
System.out.println("线程:"+ Thread.currentThread().getName()+"存款"+depositAmount);
balance += depositAmount;
System.out.println("账户余额:" + balance);
flag = true;
notifyAll();
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
2.接下来是存取款线程类(DrawThread,DepositThread)
package com.xjtu.cruise.soft.thread;
public class DepositThread extends Thread {
private Account account;
private double depositAmount;
public DepositThread(String name, Account account, double depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
}
public void run(){
for(int i=0; i<100; i++){
// System.out.println("线程:" + Thread.currentThread().getName()+"执行存款操作。");
account.deposit(depositAmount);
}
}
}
package com.xjtu.cruise.soft.thread;
public class DrawThread extends Thread {
/**
* @author Cruise
* @param args
*/
private Account account;
private double drawAmount;
public DrawThread(String name, Account account, double drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
public void run(){
for(int i=0; i<100; i++){
// System.out.println("线程:" + Thread.currentThread().getName()+"执行取钱操作。");
account.draw(drawAmount);
}
}
}
3.奉上测试类TestDraw
package com.xjtu.cruise.soft.thread;
public class TestDraw {
/**
* @author Cruise
* @param args
*/
public static void main(String[] args) {
Account account = new Account("123", 0);
new DrawThread("取钱者", account, 800).start();
new DepositThread("存款者甲",account, 800).start();
new DepositThread("存款者乙",account, 800).start();
}
}
OK, 一个使用wait和notifyAll控制线程协调的Demo完成,运行结果如下:
下面再使用条件变量的方式来协调线程的同步,具体来说就是使用Lock对象来保证同步,而不是用Synchronized关键字。
通过Lock实例来调用其newCondition()方法得到对应的Condition对象,接着使用此Condition实例的方法来保证线程的协
调。这里用到了Condition的await和signalAll方法。
将上面的Account类修改如下,其他的线程类不变。
package com.xjtu.cruise.soft.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
/**
* @author Cruise
* @param args
*/
private final Lock lock = new ReentrantLock();//定义Lock对象
private final Condition cond = lock.newCondition();//获取Lock对象对应的条件变量
private String accountNo;
//标识账户是否还有存款的
private boolean flag = false;
private double balance;
public Account(){}
public Account(String accountNo, double balance) {
this.accountNo = accountNo;
this.balance = balance;
}
public double getBalance(){
return balance;
}
public void draw(double drawAmount) {
lock.lock();//加锁
try {
if (!flag) { //如果账户没钱
cond.await();
} else {
System.out.println("线程:" + Thread.currentThread().getName()
+ "取款" + drawAmount);
balance -= drawAmount;
System.out.println("账户余额:" + balance);
flag = false;
cond.signalAll(); //唤醒该Lock对象对应的其他线程
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();//使用finally块确保释放锁
}
}
public void deposit(double depositAmount) {
lock.lock();//加锁
try{
if(flag){ //如果账户有存入的钱
cond.await();
}else{
System.out.println("线程:"+ Thread.currentThread().getName()+"存款"+depositAmount);
balance += depositAmount;
System.out.println("账户余额:" + balance);
flag = true;
cond.signalAll();//唤醒该Lock对象对应的其他线程
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{//使用finally块确保释放锁
lock.unlock();
}
}
}
运行上面的测试类,可以得到如下的运行结果:
从上面的两个运行结果可以发现,虽然两种方法都可以保证线程的同步和协调运行,但是线程的调度机制还是有些许的
差别,这点可以从上图的红框看出。当然底层的具体实现,因为不大了解,这里就不作具体的阐述了(废话,不了解当
然阐述不了,^_^),希望了解的同学能帮忙解答下,very thx!