“线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。”
我们可以用银行转账的例子加以说明:
1、甲乙两用户对银行同一账户进行操作,余额为1000元;
2、甲用户将银行余额1000元读取到本地,进行取款操作。在进行取款过程中,乙用户向银行账户进行存款300元操作并将账户余额更新,而此时甲用户进行取款是建立在原始1000元的基础上进行的。完成操作后将最终错误的800元余额更新至银行账户。。。
用程序模拟此示例:
public class TestTransferAccounts { //存取银行账户余额 static int balance = 1000; //main方法中模拟两个用户同时对该银行账户进行更新(其中一个进行取款操作,另一个对账户进行汇款操作) public static void main(String[] args) { new TestTransferAccounts().test(); } public void test(){ BankOperator bankOperator= new BankOperator(); new Thread( new Runnable() { @Override public void run() { bankOperator.addAccounts(); } } ).start(); new Thread( new Runnable() { @Override public void run() { bankOperator.reduceAccounts(); } } ).start(); //主线程等到另外两个线程操作完成后,查询账户余额进行打印。 try { Thread.sleep(1000); System.out.println("查询账户余额:" + balance); } catch (InterruptedException e) { e.printStackTrace(); } } class BankOperator{ //汇款操作方法 public void addAccounts(){ //将余额读取到本地 int myBalance = balance; //中间处理过程花费500ms。。。 try { Thread.sleep(200); myBalance += 300; } catch (InterruptedException e) { e.printStackTrace(); } //将余额更新回账目 balance = myBalance; System.out.println(Thread.currentThread().getName() + ":" + balance); } //取款操作方法 public void reduceAccounts(){ //将余额读取到本地 int myBalance = balance; //中间处理过程花费200ms。。。 try { Thread.sleep(500); myBalance -= 200; } catch (InterruptedException e) { e.printStackTrace(); } //将余额更新回账目 balance = myBalance; System.out.println(Thread.currentThread().getName() + ":" + balance); } } }
执行结果:
正如文章开头我们提到的线程互斥概念,我们需要对汇款、取款两个线程添加互斥机制,同时只能允许一个用户调用addAccounts或reduceAccounts方法,我们需要对BankOperator对象内的这两个方法添加同步的机制,防止银行将我们的账户余额搞乱。
public class TestTransferAccounts { //存取银行账户余额 static int balance = 1000; //main方法中模拟两个用户同时对该银行账户进行更新(其中一个进行取款操作,另一个对账户进行汇款操作) public static void main(String[] args) { new TestTransferAccounts().test(); } public void test(){ BankOperator bankOperator= new BankOperator(); new Thread( new Runnable() { @Override public void run() { bankOperator.addAccounts(); } } ).start(); new Thread( new Runnable() { @Override public void run() { bankOperator.reduceAccounts(); } } ).start(); //主线程等到另外两个线程操作完成后,查询账户余额进行打印。 try { Thread.sleep(1000); System.out.println("查询账户余额:" + balance); } catch (InterruptedException e) { e.printStackTrace(); } } class BankOperator{ //亏款操作方法 public void addAccounts(){ synchronized (this) { //将余额读取到本地 int myBalance = balance; //中间处理过程花费500ms。。。 try { Thread.sleep(500); myBalance += 300; } catch (InterruptedException e) { e.printStackTrace(); } //将余额更新回账目 balance = myBalance; System.out.println(Thread.currentThread().getName() + ":" + balance); } } //取款操作方法 public void reduceAccounts(){ synchronized (this) { //将余额读取到本地 int myBalance = balance; //中间处理过程花费200ms。。。 try { Thread.sleep(500); myBalance -= 200; } catch (InterruptedException e) { e.printStackTrace(); } //将余额更新回账目 balance = myBalance; System.out.println(Thread.currentThread().getName() + ":" + balance); } } } }
synchronized关键字已经帮我们实现了同步的效果,而jdk5.0为我们提供了Lock对象,来实现更为强大的同步效果。
有关synchronized与Lock的比较可以参看:
http://www.cnblogs.com/benshan/p/3551987.html#top