Java多线程(一)------卖票出现负数
作者:互联网
一、问题描述
卖票问题作为多线程入门问题,想必大家都不陌生。对于一个新入门多线程的小白来讲,在写代码的时候难免会出现各种问题。而我就出现了卖票结果出现0和负数的情况, 话不多说,截图为证:
上述是新建了五个线程,但是有四条结果是错误的。如果按照我这种写法,那么不管是新建多个个线程,最终都会有n-1条结果错误。如果你也跟我有同样的问题,就请继续看下去吧。
二、错误代码
首先我错误的写法是怎么样的呢?
上代码:
package com.example.demo.threadTest;
/**
* @ClassName: Test7
* @Author: wnn
* @Description: 三个售票窗口同时出售20张票
* @Date: 2021/10/14 14:11
*/
public class Test7 {
public static void main(String[] args) {
SellTickets sellTickets = new SellTickets();
//创建三个线程
for (int i = 1; i <= 3; i++) {
Thread thread = new Thread(sellTickets, "线程" + i);
thread.start();
}
}
}
//卖票类
class SellTickets1 implements Runnable {
static int tickets = 20;
static Object object = new Object();
@Override
public void run() {
while (tickets > 0) {
synchronized (object) {
System.out.println(System.currentTimeMillis() + "-----" + Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
tickets--;
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
输出结果如下:
可以看出,确实存在有两条记录错误。那么我错误的原因是什么呢?在卖票类里面我也进行了数字判断,tickets需要大于0,那么在最后1张票卖出后,应该不会继续执行while循环。然后结果却并非我预想的那样。
三、原因解答
经过我的苦思冥想,反复论证,我终于算是有了自己对于多线程的理解。
创建的三个线程会一起执行代码,也就是run()方法里面的while判断,发现大于0的时候会进入循环体,但是由于synchronized同步代码块的锁的存在,就意味着同一时刻只能有一个线程去卖票。
但是,重点来了。虽然进入循环体的线程可能没有占据CPU,进入同步代码块,但是却已经进入循环体,只要占有锁,即可执行ticket--的操作。
所以说,等某一线程卖完最后一张票并释放锁资源后,在此之前已经进入循环体的线程会再次争夺cpu继续卖票,直到所有进入循环体的线程都进入过同步代码块,卖过票为止。
如果对于上述的表达还是不明白的话,可以看看改编后的代码(只放出卖票类):
//卖票类
class SellTickets implements Runnable {
static int tickets = 20;
static Object object = new Object();
@Override
public void run() {
while (tickets > 0) {
System.out.println(System.currentTimeMillis() + "-----" +Thread.currentThread().getName()); //用来判断同一时刻进入的线程
synchronized (object) {
if (tickets ==0) {
System.exit(0);//退出程序
}
System.out.println(System.currentTimeMillis() + "-----" + Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
tickets--;
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
上述代码执行的执行结果,可以自己去试一下。
下面我就结果进行说明。从下图结果可以知道,在28时刻,确实有两个线程进入了循环体,但是由于线程1获取到了锁,所以进入同步代码块卖票。但是线程2会继续在循环体中争夺cpu。
然后添加if判断后,即使线程进入了同步代码块,但是由于票已卖完,就会强制执行退出操作,那么结果就不会继续减少了。
四、写在最后
不知道我的表达,你是否明白,欢迎交流。
标签:tickets,循环体,Java,卖票,代码,System,线程,------,多线程 来源: https://blog.csdn.net/weixin_44431073/article/details/120765921