卖票问题作为多线程入门问题,想必大家都不陌生。对于一个新入门多线程的小白来讲,在写代码的时候难免会出现各种问题。而我就出现了卖票结果出现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判断后,即使线程进入了同步代码块,但是由于票已卖完,就会强制执行退出操作,那么结果就不会继续减少了。
不知道我的表达,你是否明白,欢迎交流。