多线程详细解析(二) 线程的共享互斥与线程的协调
线程的共享互斥
在多线程程序里,多个线程可以自由操作,当然就有可能同时操作同一实例,这种情况有时会造成不必要的问题。
假设现在要在银行取钱,确认可用余额这部分的代码应为:
if(可用余额大于等于欲提领金额){
从可用余额中减去提领金额。
}
但是,若让两个线程同时执行,可用余额可能变成负数。
假设,账户中共有1000元,现在有A和B两个线程,都要取1000元。
当线程A和B同时操作,由于时间差,可能发生线程B夹在线程A中进行操作。
当A判断可用余额大于等于欲提领金额是,返回为TRUE,但是却并未进行提领金额,这是线程B又进行判断,仍然为TRUE。
于是线程A和线程B都从余额中扣除1000元。那么余额就变成-1000元。
这个时候就需要一种机制来防止这种问题,当一个线程在操作一个实例时,不允许其他线程同时操作这一实例。
这种机制就叫做共享互斥。
在JAVA中,处理线程的共享互斥时,会用到一个关键字synchronized.
当一个方法加上synchronized是,这个方法一次只能让一个线程执行。这种方法称为同步方法。
现在利用synchronized关键字来解决上面问题:
private int money; private String name; public Bank(int money, String name) { super(); this.money = money; this.name = name; } //存款 public synchronized void deposit(int m){ money+=m; } //取款 public synchronized String withdraw(int m){ if(money>=m){ return "取款成功"; }else{ return "取款失败,余额不足"; } }
当一个线程执行deposit()方法时,那么其他线程就不能同时执行同一实体的deposit()和withdraw()方法。
线程的协调:
假设我们想更近一步,不止是有线程在执行,其他线程就乖乖等待的简单型而已,例如:
1、若该空间有空闲则写入数据,若非空闲则等候知道出现空闲为止。
2、当该空间已空出,则通知正在等候的线程。
这是根据空间是否有空闲来进行判断的线程处理。在Java中有wait,notify和notifyall三个方法来进行处理
wait:是让线程乖乖等待的方法。obj.wait();
notify、notifyAll:唤醒等候中的线程的方法。obj.notify();obj.notifyAll();
在调用上述三个方法是,该线程必须握有欲调用线程的锁,才可以执行,否则会抛出异常。
补充说明:
notify()和notifuAll()到底使用哪一个:
notify()方法中,当正在等候的线程不止一个,规格并未说明到底调用唤醒哪一个线程,是第一个?最后一个?还是随机?
都是根据系统而定的,因此,在写程序是,程序属性最好不要写成会根据线程而有所变动。
选择notify()方法,唤醒的线程数量比较少,程序处理速度要比notifuAll()快。但是选择notify()时,若这部分处理的不好,可能会有挂掉的风险,一般来说,选择notifuAll()写出的程序代码会比notify()可靠。
除非你能确定程序员对代码的的意义和能力限度一清二楚。否则还是使用notifuAll()比较好。