Java多线程之线程的互斥处理

-

  多线程程序中的各个线程都是自由运行的,所以它们有时就会同时操作同一个实例。这在某些情况下会引发问题。例如,从银行账户取款时,余额确认部分的代码应该是像下面这样的。

  if (可用余额大于取款金额) {

  从可用余额中减掉取款金额

  }

  首先确认可用余额,确认是否允许取款。如果允许,则从可用余额上减掉取款金额。这样才不会导致可用余额变为负数。

  但是,如果两个线程同时执行这段代码,那么可用余额就有可能会变为负数。

  假设可用余额=1000元,取款金额= 1000元,那么这种情况就如下图所示:

  

  线程A 和线程B 同时操作时,有时线程B 的处理可能会插在线程A 的“可用余额确认”和“从可用余额上减掉取款金额”这两个处理之间。

  这种线程A 和线程B 之间互相竞争(race)而引起的与预期相反的情况称为数据竞争(datarace)或竞态条件(race condition)。

  这时候就需要有一种“交通管制”来协助防止发生数据竞争。例如,如果一个线程正在执行某一部分操作,那么其他线程就不可以再执行这部分操作。这种类似于交通管制的操作通常称为互斥(mutual exclusion)。这种处理就像十字路口的红绿灯,当某一方向为绿灯时,另一方向则一定是红灯。

  Java 使用关键字synchronized 来执行线程的互斥处理。

  如果声明一个方法时,在前面加上关键字synchronized,那么这个方法就只能由一个线程运行。只能由一个线程运行是每次只能由一个线程运行的意思,并不是说仅能让某一特定线程运行。这种方法称为synchronized 方法,有时也称为同步方法。

  如下所示的类就使用了synchronized 方法。Bank(银行)类中的deposit(存款)和withdraw(取款)这两个方法都是synchronized 方法。

  包含deposit 和withdraw 这两个synchronized 方法的Bank 类(Bank.java)

  如果有一个线程正在运行Bank 实例中的deposit 方法,那么其他线程就无法运行这个实例中的deposit 方法和withdraw 方法,需要排队等候。

  Bank 类中还有一个getName 方法。这个方法并不是synchronized 方法,所以无论其他线程是否正在运行deposit 或withdraw,都可以随时运行getName 方法。

  一个实例中的synchronized 方法每次只能由一个线程运行,而非synchronized 方法则可以同时由两个以上的线程运行。下图展示了由两个线程同时运行getName 方法的情况。

  

猜你喜欢