前面几篇学习了下JAVA多线程,在实际使用的过程中会发生两个不同的线程都需要访问同一个资源的现象,比如全国各地的人在12306上买同一班火车票的时候,售票方要保证这么多几乎同时发出买票请求的人得到票号连续且互不相同的票,这就需要用到多线程同步和异步方式。
一、异步 异步方式的使用比较简单,在对关键数据进行操作的方法前加修饰词synchronized即可,不同线程调用synchronized修饰的方法访问同一个资源时,只有一个线程在使用这个资源,其他线程只有等这个线程使用结束,释放资源后才能访问该资源,因此线程的访问具有独占性。打个比方,就像去上厕所,每个人独占一个包间,只有别人使用完出来了才能进去,而不用synchronized修饰词修饰就像去菜市场的某个摊位买菜,摊主有5个番茄,一开始你(代表线程甲)和摊主讲好价买下这五个番茄,但突然中间插入个小偷(代表线程乙)偷走了2个番茄,你的预期输出是5,但是实际输出确是3,这是多线程共享资源时时常遇到的问题。
二、同步 异步的方法较为被动,调用sleep()方法也占用系统资源,而同步使用Object类里的wait()和notify()方法解决共享冲突,这两个方法被JAVA里的所有对象继承,所以JAVA天生支持同步。
以下面会贴出的代码为例: 有两个线程:家长线程和学生线程,他们共同拥有一个银行帐户,起初银行帐户资金为0,学生每隔2s检查一次帐户,若帐户资金不为0则取出50,家长每隔12s检查一次若帐户资金为0则存入200。 这里除了把帐户类的getMoney()和saveMoney()两个方法限制为synchronized之外,还使用wait()和notify()方法让这两个线程互相通知。 代码如下:
//Accout.java
public class Accout {
public int money;
public Accout() {
money = 0;
}
public synchronized void saveMoney() {
System.out.println("准备存钱!");
try {
if (money != 0) {
System.out.printf("帐户仍有余额!");
notify(); //提醒存钱
wait();
}else {
money = 200;
System.out.println("存入:" + money);
}
}catch(Exception e) {}
}
public synchronized void getMoney() {
System.out.println("准备取钱!");
try {
if (money == 0) {
System.out.println("帐户余额为零");
wait();
} else {
money -= 50;
System.out.println("取出:50 余额:" + money);
notify(); //提醒取钱
}
}catch(Exception e) {}
}
}
//ParentThread.java
public class ParentThread extends Thread{
Accout act;
public ParentThread (Accout act) {
this.act = act;
start();
}
public void run() {
try {
while(true) {
Thread.sleep(12000);
act.saveMoney();
}
}catch(Exception e) {}
}
}
//StudentThread.java
public class StudentThread extends Thread{
Accout act;
public StudentThread (Accout act) {
this.act = act;
start();
}
public void run() {
try {
while (true) {
Thread.sleep(2000);
act.getMoney();
}
}catch(Exception e) {}
}
}
/**
* TestMulThread.java
* @author Avery liu
* 测试多线程访问冲突问题
*/
public class TestMulThread {
public static void main(String[] args) {
Accout act = new Accout();
StudentThread st = new StudentThread (act);
ParentThread pt = new ParentThread (act);
}
}