multithreading--线程不安全的案例,及其采用synchronized的解决办法
2015-10-29 20:39
387 查看
一:一个线程不安全的情况举例
public class J_xianchenganqaun { /**多线程不可控因素:1,线程调度将时间片段分配给哪个线程;2,时间片段的具体时间。 * @param args */ public static void main(String[] args) { final Table table=new Table(); Thread t1=new Thread(){ public void run(){ while(true){ /** * 当beans=0时,run调用了getBean方法, * 抛出了一个未解决的异常,该线程被杀掉 */ int bean=table.getBean(); System.out.println(getName()+":"+bean); Thread.yield(); } } }; Thread t2=new Thread(){ public void run(){ while(true){ int bean=table.getBean(); System.out.println(getName()+":"+bean); Thread.yield(); } } }; t1.start(); t2.start(); } } class Table{ private int beans=20; public int getBean(){ if(beans==0){ throw new RuntimeException("没豆了"); } Thread.yield();//当前线程主动放弃CPU时间片,且在当前运行的位置暂停 return beans--; /** * 线程不安全情况: * 1,当程序运行到beans=1时,线程t1调用getBean方法 * 2,此时beans=1!=0;所以不抛出异常 * 3,接着,t1运行到Thread.yield();自动放弃CPU时间片,t1在这个位置暂停; * 4,t1放弃CPU时间片后,t2调用getBean方法,此时仍然beans=1!=0 * 5,接着t2运行到Thread.yield(),t2也自动放弃时间片,t2也在这个位置暂停; * 6,t2放弃后,t1接着暂停的位置继续运行,运行到return语句,返回1,beans变为0,但是错过了抛出异常 * 7,接着,t1的时间片过了,t2接着暂停的位置继续执行,t2执行到return,但是此时beans=0,所以返回0,beans变为-1 * 8,从此以后beans逐渐往负无穷靠拢,背离了现实意义。 */ } }
二:上面线程不安全情况的解决办法
/** * 多线程并发访问同一资源时,会产生线程安全问题。 * 解决多线程并发安全问题的办法是:将异步的操作变为同步的. * 所谓同步:按先后顺序执行 * 所谓异步:根本就没有先后的概念 * @author Administrator * */ public class J_y { public static void main(String[] args) { final Table3 table=new Table3(); Thread t1=new Thread(){ public void run(){ while(true){ /** * 当beans=0时,run调用了getBean方法, * 抛出了一个未解决的异常,该线程被杀掉 */ int bean=table.getBean(); System.out.println(getName()+":"+bean); Thread.yield(); } } }; Thread t2=new Thread(){ public void run(){ while(true){ int bean=table.getBean(); System.out.println(getName()+":"+bean); Thread.yield(); } } }; t1.start(); t2.start(); } } class Table3{ private int beans=20; /** * synchronized的作用是当线程t1调用该方法时,会加把锁,实际上是给对象加把锁 * 这样即使在getBean方法内部,t1的时间片用完了,t2也只能在getBean方法外面等着 * 也进不去,等时间片再次轮到t1,t1会继续执行,就是说只能有一个线程进入直到执行完位置,即将异步的操作变为同步的 * @return */ synchronized public int getBean(){ if(beans==0){ throw new RuntimeException("没豆了"); } Thread.yield();//当前线程主动放弃CPU时间片 return beans--; } }
三:更加高效的解决方式
上面使用synchronized同步了整个方法,但是很多时候,只需要同步方法的局部就可解决安全问题,另外因为同步的范围小了,多线程并发执行的效率也会增加,因此可以采用同步块的方式解决线程安全问题。public class K_jianshiqi { /** * @param args */ public static void main(String[] args) { final Table1 table=new Table1(); Thread t1=new Thread(){ public void run(){ while(true){ int bean=table.getBean(); System.out.println(getName()+":"+bean); Thread.yield(); } } }; Thread t2=new Thread(){ public void run(){ while(true){ int bean=table.getBean(); System.out.println(getName()+":"+bean); Thread.yield(); } } }; t1.start(); t2.start(); } } class Table1{ private int beans=20; public int getBean(){ /** * 同步块可以有效的缩小同步范围,从而提高并发执行效率。 使用同步块,必须要保证多线程看到的同步监视器这个对象是同一个! */ synchronized(this){//这里的this是table,t1和t2看到的是同一个,也可以换成任意字符串,但是不可以是new Object(),因为t1和t2会创建!=的对象 if(beans==0){ throw new RuntimeException("没豆了"); } Thread.yield(); return beans--; } } }
相关文章推荐
- iOS:新浪微博OAuth认证
- Matlab将图像拼接成视频
- MobileVLC with HTTP stream
- SQL常用语句
- javascript实现的简单扩大横条
- hive service的启动及关闭
- 【Qt多线程之线程的等待和唤醒】QWaitCondition
- 如果使用ImageLoder加载图片
- 单例设计模式
- C#—构造函数和析构函数
- 在equals中使用getClass进行类型判断
- Centos6.6 yum安装docker
- win7/8系统下VS工程无法访问网络驱动器中的文件
- 欢迎使用CSDN-markdown编辑器
- 覆写equals方法时不要识别不出自己,equals应该考虑null值情景
- Android 简单实现倒计时CountDownTimer
- Cocoapods简易使用指南(以方便导入POP动效库)
- Volley的使用
- 终端分屏与vim分屏
- Linux下Firefox打开文件jnlp文件