博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
synchronized+Integer模拟火车票预售,出现的问题总结
阅读量:4179 次
发布时间:2019-05-26

本文共 3638 字,大约阅读时间需要 12 分钟。

实现火车票预售,必然会出现线程安全问题,开始用原子类AtomicInteger,后面觉得Lock也可以实现,用Lock 也是实现了下,在用Synchronized实现的时候,出现了下面的问题;

1.首先大家抢票,那我定义一个Integer类型的count   用来存放火车票数(从这里开始就已经为后面的代码埋下了雷啊!)

2.创建抢票线程类,在这个类的run方法中对count加锁,进行票减少操作(这里就是最大的隐患)

3.启动4个窗口,对火车票进行抢售

代码如下:

public class TikcetTestSync {	private volatile Integer count = 200;	@Test	public void test() throws InterruptedException {		TikcetRunnable tr = new TikcetRunnable();		Thread t1 = new Thread(tr, "窗口A");		Thread t2 = new Thread(tr, "窗口B");		Thread t3 = new Thread(tr, "窗口C");		Thread t4 = new Thread(tr, "窗口D");		t1.start();		t2.start();		t3.start();		t4.start();		Thread.currentThread().join();	}	public class TikcetRunnable implements Runnable {		@Override		public void run() {						while (count > 0) {				synchronized (count) {//count加锁和this加锁区别?//					System.out.println(System.identityHashCode(count));					if (count > 0) {							System.out.println(Thread.currentThread().getName() + "  销售了第  " + (count--) + "  张票");						try {							Thread.sleep(200);//							System.out.println(Thread.currentThread().getName()+"开始继续卖票");					    } catch (InterruptedException e) {					    	e.printStackTrace();					    }					}									}											}		}	}}

备注:代码里,我觉得每个线程直接买完票,就释放锁,太不贴近现实,所以休眠200毫秒,然后再释放锁,因为sleep是不会释放锁的

但是运行结果,事与愿违:

可以看到16这张票,重复卖了。。。。。。。这是什么原因呢?锁没加成功?还是其他什么原因?

代码进行修改,把synchronized(count)修改为synchronized(this),运行结果就正确了,看来是count的问题  并不是加不了锁   因此在代码中添加以下代码

public class TikcetRunnable implements Runnable {		@Override		public void run() {						while (count > 0) {				synchronized (count) {					System.out.println(System.identityHashCode(count));					if (count > 0) {							System.out.println(Thread.currentThread().getName() + "  销售了第  " + (count--) + "  张票");						try {							Thread.sleep(200);					    } catch (InterruptedException e) {					    	e.printStackTrace();					    }					}									}			}		}	}

运行结果是:

这里添加了一句System.out.println(System.identityHashCode(this));目的是打印每一个获得锁的对象的系统hashcode的值,发现每次获得的都不一样,引起这个主要原因是:count--操作的时候等价于  count = new Integer(count-1)  ,这里可以看到,count是重新创建的对象,虽然很多地方说是integer值小于127会从常量池中获得,但是整个的逻辑应该还是:加入A窗口先获得了count=200的锁,将存放200的内存锁住,然后count -- 后  count=199,A窗口锁定的还是200那块内存,而这个时候如果B线程获得了使用权限,那B线程看到count=199并没有加锁,那就可以加锁访问,因此B对存放199的这块内存加锁,一此类推,就会出现A窗口加锁还没释放锁,B窗口已经开始加锁,这是因为锁对象一直在变换   但是还是不可能出现一张票多买的问题,那我们继续找原因;

while (count > 0) {				synchronized (count) {

其实问题原因出在这里,线程先读取了count的值,然后才加锁,有可能只读到值,还未加锁,时间片到期,但是下次是顺序执行,下一次过来的时候count的值是上次获得的值,因此会出现一次票多次销售!问题到这里基本都解决了,解决问题的代码如下:

public class TikcetTestSync {	private volatile Integer count = 200;	@Test	public void test() throws InterruptedException {		TikcetRunnable tr = new TikcetRunnable();		Thread t1 = new Thread(tr, "窗口A");		Thread t2 = new Thread(tr, "窗口B");		Thread t3 = new Thread(tr, "窗口C");		Thread t4 = new Thread(tr, "窗口D");		t1.start();		t2.start();		t3.start();		t4.start();		Thread.currentThread().join();	}	public class TikcetRunnable implements Runnable {		@Override		public void run() {			synchronized (count) {				while (count > 0) {					if (count > 0) {						System.out.println(Thread.currentThread().getName() + "  销售了第  " + (count--) + "  张票");						try {							Thread.sleep(200);							System.out.println(Thread.currentThread().getName()+" 本次售票操作结束,等待下一次售票");					    } catch (InterruptedException e) {					    	e.printStackTrace();					    }					}									}			}		}	}}

到此,给count加锁的问题解决了,总结一下,当我先循环判断条件,再加锁,并且在加锁过程中调用sleep方法,甚至在sleep方法后打印此窗口本次售票结束,会出现两个让人费解的问题:

1.大家都知道获得锁之后,sleep方法是不会放弃锁的,但是如果运行代码,会发现,A窗口未完成本次售票,B窗口就开始销售票,主要原因是Integer类型的变量,在进行count--操作的时候,会新建一个对象,因此就出现AB线程加锁对象对应的内存地址不一样,因而不用A释放锁,B才能加锁

2.出现一张票多次销售,主要原因是加锁和读值得先后顺序有问题。

希望大家以此为戒!当然方便的话,直接对this,当前的线程对象加锁,对象加锁了,里面成员变量也是被锁住的,通过提高加锁的粒度,简化程序的复杂性!

你可能感兴趣的文章
7 面 Google,还是失败了....
查看>>
技术大佬们都是怎么学习的?
查看>>
45 年编程经验告诉我的技术真相
查看>>
Python除了不能帮你生孩子,什么都能做!
查看>>
考研失败了,该何去何从?
查看>>
聊一聊我在 B 站上自学编程的经历吧
查看>>
酸了!会这个技能的 AI 工程师年薪至少35W起!
查看>>
靠,竟然有如此沙雕的代码注释!
查看>>
大一新生开发的小工具火了,网友:我好菜
查看>>
为什么大学生学点Python容易拿高薪?
查看>>
小米官方良心出品,永久免费无广告!!
查看>>
新版 Win 10 来了。网友:要被丑哭了……
查看>>
无意中发现了一位清华大佬的代码模版
查看>>
2021 年第一款神器,非它莫属!!
查看>>
太绝了!一篇文章说清了Python学习的全部路线!
查看>>
面试前,千万要注意这件事!!!
查看>>
最新通知:国内一大批网站和APP将告别弹窗广告!
查看>>
GitHub 开源神器:图片秒变文件
查看>>
老师吴恩达,身家又增 20 亿!
查看>>
基金跌了怎么办?来听听大咖教你怎么做!
查看>>