Java 多线程(五) 多线程的同步
2015-12-26 10:28
561 查看
Java 多线程(五) 多线程的同步
为什么要引入同步机制
在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源。必须对这种潜在资源冲突进行预防。解决方法:在线程使用一个资源时为其加锁即可。
访问资源的第一个线程为其加上锁以后,其他线程便不能再使用那个资源,除非被解锁。
程序实例
用一个取钱的程序例子,来说明为什么需要引入同步。在使用同步机制前,整体程序如下:
public class FetchMoneyTest { public static void main(String[] args) { Bank bank = new Bank(); Thread t1 = new MoneyThread(bank);// 从银行取钱 Thread t2 = new MoneyThread(bank);// 从取款机取钱 t1.start(); t2.start(); } } class Bank { private int money = 1000; public int getMoney(int number) { if (number < 0) { return -1; } else if (number > money) { return -2; } else if (money < 0) { return -3; } else { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } money -= number; System.out.println("Left Money: " + money); return number; } } } class MoneyThread extends Thread { private Bank bank; public MoneyThread(Bank bank) { this.bank = bank; } @Override public void run() { System.out.println(bank.getMoney(800)); } }
程序中定义了一个Bank类,其中包含了用户存储的钱(1000元),然后用两个线程进行取钱操作,可以看到尽管Bank类中的getMoney()方法对取钱数目与存款数据进行了判断,但是执行后,结果输出两个800,表明从两个线程中都成功地取出了800元钱。
这是为什么呢?因为getMoney()方法中有一些逻辑判断,进入最后一个else语句块后,有一个简短的休眠,那么在第一个线程休眠的过程中,第二个线程也成功进入了这个else语句块(因为存款的钱还没有取走),当两个线程结束休眠后,不再进行逻辑判断而是直接将钱取走,所以两个线程都取到了800元钱,此时money为负600。
需要注意这里并不能确定哪一个线程是第一个线程,哪一个线程是第二个线程,先后顺序是不定的。
在getMoney()方法中加入打印语句输出剩余的钱数,可以看到输出为剩余钱数为200,-600,或-600,-600。这是不一定的,因为可能在第一次输出剩余钱数之前,另一个线程可能还没有将钱取走,也可能已经取走。
解决办法
解决办法:在getMoney()方法上加上关键字synchronized。即程序改动后如下:(只是加了一个关键字)
改正后的程序例子
public class FetchMoneyTest { public static void main(String[] args) { Bank bank = new Bank(); Thread t1 = new MoneyThread(bank);// 从银行取钱 Thread t2 = new MoneyThread(bank);// 从取款机取钱 t1.start(); t2.start(); } } class Bank { private int money = 1000; public synchronized int getMoney(int number) { if (number < 0) { return -1; } else if (number > money) { return -2; } else if (money < 0) { return -3; } else { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } money -= number; System.out.println("Left Money: " + money); return number; } } } class MoneyThread extends Thread { private Bank bank; public MoneyThread(Bank bank) { this.bank = bank; } @Override public void run() { System.out.println(bank.getMoney(800)); } }
public class FetchMoneyTest { public static void main(String[] args) { Bank bank = new Bank(); Thread t1 = new MoneyThread(bank);// 从银行取钱 Thread t2 = new MoneyThread(bank);// 从取款机取钱 t1.start(); t2.start(); } } class Bank { private int money = 1000; public synchronized int getMoney(int number) { if (number < 0) { return -1; } else if (number > money) { return -2; } else if (money < 0) { return -3; } else { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } money -= number; System.out.println("Left Money: " + money); return number; } } } class MoneyThread extends Thread { private Bank bank; public MoneyThread(Bank bank) { this.bank = bank; } @Override public void run() { System.out.println(bank.getMoney(800)); } }
此时输出结果如下:
Left Money: 200
800
-2
表明第一次取款800元后,剩余200元,当另一个线程再去取的时候,已经不能再取钱了。
一个线程开始执行取钱的方法之后就阻止了其他线程再去执行这个方法,直到本线程结束,其他线程才有访问权利。
当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
Java中的每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时,将该对象上锁,其他任何线程都无法再去访问该对象的synchronized方法了。直到之前的那个线程执行方法完毕后(或者是抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。
注意这时候是给对象上锁,如果是不同的对象,则各个对象之间没有限制关系。
参考资料
圣思园张龙老师Java SE系列视频教程。来源:http://www.cnblogs.com/mengdd/archive/2013/02/16/2913680.html
相关文章推荐
- java应用程序-系统托盘
- 《Java Web 程序开发入门》知识总结
- 死磕Spring系列之四 BeanDefinition接口、BeanFactory接口
- Java 多线程(四) 多线程访问成员变量与局部变量
- [javase学习笔记]-7.6 this关键字之原理
- java中long和double类型操作的非原子性探究
- java 打包下载文件
- Java 多线程(三) 线程的生命周期及优先级
- 5本最佳的Java面向对象理论和设计模式的书籍
- Java_ststic_静态块
- Java_String_字符串操作
- java学习
- Spring学习第三天——Spring MVC,使用注解方式,intellij的代码说明,ajax,文件的上传下载
- Eclipse右键New菜单项的自定义设置
- MyEclipse8.5破解方法
- SpringMVC源代码深度分析DispatcherServlet核心的控制器(初始化)
- 解决Myeclipse下Debug出现Source not found以及sql server中导入数据报错
- opencv java中打开视频问题
- XMPP之openfire运行时需要32bit,但是安装的Java却是64bit
- Java基础--正则表达式