JavaSE进阶
15.5.4 练习2
模拟银行取钱的问题
1.定义一个Account类
1)该Account类封装了账户编号(String)和余额(double)两个属性
2)设置相应属性的getter和setter方法
3)提供无参和有两个参数的构造器
4)系统根据账号判断与用户是否匹配,需提供hashCode()和equals()方法的重写
2.提供一个取钱的线程类
1)提供了Account类的account属性和double类的取款额的属性
2)提供带线程名的构造方法
3)run()方法中提供取钱的操作
3.在主类中创建线程进行测试。考虑线程安全问题。
public class Account {
private String accountId;
private double balance;
public Account() {
}
public Account(String accountId, double balance) {
this.accountId = accountId;
this.balance = balance;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public String toString() {
return "Account [accountId=" + accountId + ", balance=" + balance + "]";
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((accountId == null) ? 0 : accountId.hashCode());
long temp;
temp = Double.doubleToLongBits(balance);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Account other = (Account) obj;
if (accountId == null) {
if (other.accountId != null)
return false;
} else if (!accountId.equals(other.accountId))
return false;
if (Double.doubleToLongBits(balance) != Double.doubleToLongBits(other.balance))
return false;
return true;
}
public class WithDrawThread extends Thread {
Account account;
// 要取款的额度
double withDraw;
public WithDrawThread(String name, Account account, double amt) {
super(name);
this.account = account;
this.withDraw = amt;
}
public void run() {
synchronized (account) {
if (account.getBalance() > withDraw) {
System.out.println(Thread.currentThread().getName() + ":取款成功,取现的金额为:" + withDraw);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setBalance(account.getBalance() - withDraw);
} else {
System.out.println("取现额度超过账户余额,取款失败");
}
System.out.println("现在账户的余额为:" + account.getBalance());
}
}
}
public class WithDrawThread extends Thread {
Account account;
// 要取款的额度
double withDraw;
public WithDrawThread(String name, Account account, double amt) {
super(name);
this.account = account;
this.withDraw = amt;
}
public void run() {
synchronized (account) {
if (account.getBalance() > withDraw) {
System.out.println(Thread.currentThread().getName() + ":取款成功,取现的金额为:" + withDraw);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setBalance(account.getBalance() - withDraw);
} else {
System.out.println("取现额度超过账户余额,取款失败");
}
System.out.println("现在账户的余额为:" + account.getBalance());
}
}
}
}public class TestWithDrawThread {
public static void main(String[] args) {
Account account = new Account("1234567", 10000);
Thread t1 = new WithDrawThread("小明", account, 8000);
Thread t2 = new WithDrawThread("小明's wife", account, 2800);
t1.start();
t2.start();
}
}
15.6 线程池
系统启动一个新线程的成本是比较高的,因为它涉及与os交互。这种情况下,系统启动时即创建大量空闲的线程,就可以很好地提高性能,尤其是当程序需要创建大量生存期很短暂的线程时。
除此之外,使用线程池可以有效地控制系统中并发线程的数量。避免因并发创建的线程过多,导致系统性能下降,JVM崩溃。
Java 5以前,需要手动创建自己的线程池;Java 5开始,新增了Executors工厂类产生线程池。
使用线程池执行线程任务的步骤如下:
1.调用Executors 类的静态方法newFixedThreadPool(int nThreads),创建一个可重用的、具有固定线程数的线程池ExecutorService对象
2.创建Runnable实例,作为线程执行任务
3.调用ExecutorService对象的submit()提交Runnable实例
4.调用ExecutorService对象的shutDown()方法关闭线程池。