xl_echo编辑整理
之前的文章我们讲到了,四个电影院窗口同时出售50张彩票的问题。在实现的过程中,我们使用Tread继承,达到了需求的效果,但是也提出了一部分问题。这里我们先使用Runnable进行改造之前的程序,实现效果,然后在来阐述之前的问题。
实现Tread完成需求代码如下:
package com.example.mybatisplusdemo.test;
/**
* @Author xl_echo
* @Date 2018/8/7 下午1:54
**/
public class Window extends Thread {
//售票窗口
private final String name;
//有50张电影票
private static final int MAXTicket = 10000;
public Window(String name) {
this.name = name;
}
private static int index = 1;
@Override
public void run(){
while (index <= MAXTicket){
System.out.println("窗口" + name + "当前是第" + (index++) + "张票");
}
}
public static void main(String[] args) {
Window window1= new Window("1号窗口");
window1.start();
Window window2= new Window("2号窗口");
window2.start();
Window window3= new Window("3号窗口");
window3.start();
Window window4= new Window("4号窗口");
window4.start();
}
}
使用Runnable改造,改造后代码如下
package com.example.mybatisplusdemo.test;
/**
* @Author xl_echo
* @Date 2018/8/8 上午10:53
**/
public class Test implements Runnable {
private int index = 1;
private final static int MAX = 50;
@Override
public void run() {
while (index <= MAX){
System.out.println("窗口" + Thread.currentThread() + "当前是第" + (index++) + "张票");
try{
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
final Test test = new Test();
Thread windows1 = new Thread(test, "一号窗口");
Thread windows2 = new Thread(test, "二号窗口");
Thread windows3 = new Thread(test, "三号窗口");
Thread windows4 = new Thread(test, "四号窗口");
windows1.start();
windows2.start();
windows3.start();
windows4.start();
}
}
输出结果这里就不展示了,因为这里的结果和之前程序的结果是一样的,如果index增大的时候一样会出现线程安全问题。
可能有朋友会发现我们没有使用static修饰index,会不会是没有修饰造成的安全问题,其实不然。比如,有一个线程拿到了index,这个时候准备执行方法,刚好另外一个线程也拿到了index,同时也准备执行方法,这个时候就会造成两个窗口卖了同一张票。2而我们的输出就出现了一下图片中的输出相同票数的情况。当我们的某一个线程拿到了最后一张票的时候,index=50了,刚好该线程进入了休眠。然后其他两个线程进入,也获取到了index发现并大于max,这个时候就会执行卖票,当这边后进入的程序执行完成,前面的程序再次唤醒执行,就会出现卖了51张票。
解决办法:使用synchronized关键字。
- 但是这个关键字加在什么地方呢?
比如:加在run()方法上面,你会发现基本都是一号窗口买完了所有的票。因为同步锁在第一条线程进入后,就锁死了该方法的执行,必须要第一条线程执行完成该方法才能够进入第二线程,所以等第二条进入的时候就会发现index已经大于50了,自然输出结果就是一条线程执行完了所有出票。
public synchronized void run() {...}
比如:加在while循环里面,使用synchronized(this){...}
括上方法里面的所有代码,这个时候你会发现,基本每次都多出了三张票。因为:while判断的下一步有四条线程执行,到最后一张票的时候,所有线程都拿到了,并且由于锁的关系,每条线程都执行一次,所以这个时候就多卖了三张。
while (index <= MAX) {
synchronized (this) {
...
}
}
以上程序最终的解决办法:
将同步锁加在休眠上面,我们会发现我们效果达到了,线程安全问题解决了
synchronized (this) {
Thread.sleep(100);
}
这是什么原因呢?
因为我们加在Thread.sleep(100);
上面的时候,我们的四个线程只有有一个进入休眠,其他的就会进入等待,刚好这时候进入休眠等待的线程就拦截了后的线程进行下一步操作。所以当我们的线程进入休眠的时候,前面的index叠加就已经完成了。
当我们的index叠加到46的时候,执行输出和index再次叠加,刚好每条线程叠加一次,线程进入休眠,后面的想再次叠加也会被拦截。当最前面的线程唤醒之后,后面的线程都已经执行完成了,index自然是要比MAX大。
当然这里也提出一个疑问?大家可以一起思考
那就是index叠加到46的时候,再次循环,第一条线程将index叠加为47,进入休眠,后面的线程在休眠时间内还没有完成最后的三张票出售。这个时候就会不会出现数据安全问题?欢迎大家跟我联系。