实现Runnable解决多线程数据安全问题

xl_echo编辑整理,欢迎转载,转载请声明文章来源。更多IT、编程案例、资料请联系QQ:1280023003,加群298140694。百战不败,依不自称常胜,百败不颓,依能奋力前行。——这才是真正的堪称强大!!


之前的文章我们讲到了,四个电影院窗口同时出售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,进入休眠,后面的线程在休眠时间内还没有完成最后的三张票出售。这个时候就会不会出现数据安全问题?欢迎大家跟我联系。

猜你喜欢

转载自blog.csdn.net/xlecho/article/details/81506015