当前位置:   article > 正文

17Java的多线程抢票程序(小改)_java使用多线程模拟抢票功能,两个线程分别对应两个抢票人员等待放票,线程启动后分

java使用多线程模拟抢票功能,两个线程分别对应两个抢票人员等待放票,线程启动后分

多线程

模拟抢票的程序
package ThreadStudy;

/**模拟抢票功能
 * 抢票需要注意的是多个线程来抢夺同一份资源。一份资源,多个代理。
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/17 14:39
 */
public class Web12306 implements Runnable {
    private int ticketNumbers = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNumbers <= 0) {
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (Thread.currentThread().getName() == "Agent2") {
                System.out.println("           " + Thread.currentThread().getName() + "->" + ticketNumbers--);
            } else if (Thread.currentThread().getName() == "Agent3") {
                System.out.println("                      " + Thread.currentThread().getName() + "->" + ticketNumbers--);
            } else {
                System.out.println(Thread.currentThread().getName() + "->" + ticketNumbers--);
            }
        }
    }

    public static void main(String[] args) {
        //一份资源
        Web12306 web1 = new Web12306();
        System.out.println(Thread.currentThread().getName());
        //多个代理
        new Thread(web1, "Agent1").start();
        new Thread(web1, "Agent2").start();
        new Thread(web1, "Agent3").start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

运行结果:

run方法里面让线程休眠200ms是为了让程序运行的过程可视化,不然执行的太快就看不到多线程的效果了。

从运行结果可以看到,首先输出的是主线程main的线程名称,然后这里使用的是创建一个线程类Web12306类的对象,使用这个对象开启了三个线程,这三个线程访问的是同一个类的对象的资源,也就是**“一份资源,多个代理”**,但是我们可以看到,Agent3=0的时候票已经卖完了,本应该结束卖票,但是Agent1仍然访问到了ticketNumbers并且做了减一操作,这显然是不正常的,为什么?如何避免这种现象?

下面是模拟龟兔赛跑的程序
package ThreadStudy;

/**模拟龟兔赛跑
 * 龟兔赛跑跟抢票不同,抢票是不同的代理访问/修改同一份资源,龟兔赛跑是两个对象(龟和兔)拥有各自的计步器,
 * 但是拥有共同的属性:获胜者(winner),因为只能有一个获胜者,所以两个对象都能够访问/修改同一份资源(winner),
 * 也就是说龟兔赛跑与抢票的相同点就是他们都有公共的属性。
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/17 16:30
 */
public class RabbitRace implements Runnable {
    private String winner;

    @Override
    public void run() {
        for (int steps = 1; steps <= 10; steps++) {
            System.out.println(Thread.currentThread().getName() + "->" + steps);
            boolean flag = gameOver(steps);
            if (flag) {
                break;
            }
        }
    }

    public boolean gameOver(int steps) {
        if (winner != null) {//这里用的是反过来的思想,这个思想在Java编程中很常见,首先判断条件不成立,然后再判断条                               //成件立该怎么做
            return true;
        } else {
            if (steps == 10) {
                winner = Thread.currentThread().getName();
                System.out.println("Winner is "+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        RabbitRace race = new RabbitRace();
        new Thread(race,"tortoise").start();//注意,这里是开启了两个线程,虽然他们共用一个winner变量
        new Thread(race,"rabbit").start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

运行结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jGkeH8Ad-1630078908268)(E:(A)]PostgraduateFlies\JavaLearning\TyporaFiles\第二阶段\25Java的多线程抢票.assets\image-20210417172337363.png)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3WNPaRfi-1630078908276)(E:(A)]PostgraduateFlies\JavaLearning\TyporaFiles\第二阶段\25Java的多线程抢票.assets\image-20210417172400582.png)

每次运行的结果都可能不一样,而且有时候一个线程运行完成了,另一个线程才刚开始运行,有时候交替运行。

但是对于这个程序我有点问题,我觉得对于判断什么时候跑步结束的条件,这个程序是用了一个gameOver方法,我认为不好,可以直接在run方法中写成这样:

    @Override
    public void run() {
        for (int steps = 1; steps <= 10; steps++) {
            System.out.println(Thread.currentThread().getName() + "->" + steps);
            if (steps == 10) {
                winner = Thread.currentThread().getName();
                System.out.println("Winner is "+winner);
                break;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

但是上面程序的运行结果如下:

两个线程同时运行没错,但是每次都会出现两个胜利者的情况,这显然不对。

分析原因:线程开启后,两个线程同时运行,两个线程的run方法中的for循环都开始循环输出,虽然都在for循环中设置了循环中止条件,但是这个胜利者中止条件是**“线程独立的”**,姑且就这样叫,意思是两个线程的胜利者终止条件是相互独立的,也就是说一个线程条件满足并break不影响另一个线程结束,tortoise胜利了给winner赋值之后,rabbit仍然可以给winner赋值,问题就出在这里,还有一个问题就是,for循环本来就10个,if条件语句的终止条件也是10所以说这个if语句没用,肯定会输出。

假如写成一个返回值为布尔值的方法,在方法中判断胜利者条件,并且给winner赋值,所以只有其中一个线程满足了终止条件,这个线程就停止运行,返回并显示winner,同时另一个线程同样终止,对于这个现象我理解的是这两个线程是来源于同一个线程类的对象,其中一个把run方法关闭了,另一个也就随之停止了

上面这个解释不对,一个线程赢了另一个线程也随之停止的原因是,赢的那个线程把共同的参考变量属性winner赋值使之非空,所以另一个线程在判断是否有胜利者的时候发现winner非空,所以这个线程就停止了,而且输出操作可能已经做完了,所以会出现winner已经出现,另一个线程还输出了一个值。

每个线程每跑一步就会判断一次有没有产生胜利者,所以如果已经有胜利者,另一个也就不会再跑了。

哦!我明白了,我知道出现两个胜利者是是因为这种写法有bug,这段代码拿下来:

@Override
public void run() {
    for (int steps = 1; steps <= 10; steps++) {
        System.out.println(Thread.currentThread().getName() + "->" + steps);
        if (steps == 10) {
            winner = Thread.currentThread().getName();
            System.out.println("Winner is "+winner);
            break;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

if (steps == 10)条件只是判断步数steps是否达到总数,达到总数就break循环,关键是两个线程都能够达到总数,换句话说,有没有这个if语句,输出结果都一样,都会出现两个winner,所以说这两个线程缺少一个统一的暂停标准,而使用gameOver方法,这个方法里面的这段代码:if (winner != null) { return true; } 就是提供了这个统一的标准。

if (winner != null) { return true; },winner不等于null说明此时已经有了胜利者,直接返回true,终止程序,这一句很关键,如果其中一个线程胜利了,那么winner就不是null, 所以直接停止运行,同时停止另外一个线程,因为这两个线程共享的一个对象的winner属性

所以,如果不用gameOver方法,那么run方法可以写成如下形式,运行结果正确:

@Override
    public void run() {
        for (int steps = 1; steps <= 10; steps++) {
            System.out.println(Thread.currentThread().getName() + "->" + steps);
            if (winner != null) {
                break;
            } else {
                if (steps == 10) {
                    winner = Thread.currentThread().getName();
                    System.out.println("Winner is " + winner);
                    break;
                }
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

如果兔子每跑10步就休息一下,那么程序改为:

package ThreadStudy;

/**
 * 模拟龟兔赛跑
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/17 16:30
 */
public class RabbitRace implements Runnable {
    private String winner;

    @Override
    public void run() {
        for (int steps = 1; steps <= 100; steps++) {
            System.out.println(Thread.currentThread().getName() + "->" + steps);
            if (Thread.currentThread().getName().equals("rabbit")&&steps%10==0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (winner != null) {
                break;
            } else {
                if (steps == 100) {
                    winner = Thread.currentThread().getName();
                    System.out.println("Winner is " + winner);
                    break;
                }
            }
//            boolean flag = gameOver(steps);
//            if (flag) {
//                break;
//            }
        }
    }

    public boolean gameOver(int steps) {
        if (winner != null) {//winner不等于null说明此时已经有了胜利者,直接返回true,终止程序,这一句很关键,如果其中一个线程胜利了,那么winner就不是null,
            // 所以直接停止运行,同时停止另外一个线程,因为这两个线程共享的一个对象的winner属性
            return true;
        } else {//如果此时没有胜利者,那么就进行判断是否跑完全程
            if (steps == 100) {
                winner = Thread.currentThread().getName();
                System.out.println("Winner is " + winner);
                return true;
            }
            return false;
        }
    }

    public static void main(String[] args) {
        RabbitRace race = new RabbitRace();
        new Thread(race, "tortoise").start();
        new Thread(race, "rabbit").start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/632107
推荐阅读
相关标签
  

闽ICP备14008679号