刘小爱v 自学Java第33天:用一个案例向你说明什么是线程同步?

今天是我自学Java的第33天 。
感谢你的陪伴 , 谢谢你 。
话不多说 , 开始今天的学习:线程同步 。
刘小爱v 自学Java第33天:用一个案例向你说明什么是线程同步?
文章图片
想必很多小伙伴应该都经历过去火车站买票的情况 。
现有一个案例:火车站有3个售票窗口 , 一共有100张票要卖 , 3个窗口同时卖 。
对于这种情形 , 如何使用Java代码来实现?
一、多窗口卖票案例
根据我们这几天的学习 , 很显然要创建三个线程来解决这种情况 , 我们选择使用实现Runnable接口的这种方式来创建线程:
刘小爱v 自学Java第33天:用一个案例向你说明什么是线程同步?
文章图片
①创建一个类MyRunnable , 实现Runnable接口 。
②有100张票要售卖 。
也就是说类中有一个成员变量是100 。
③重写run方法 。
④创建一个循环语句:
因为需要一直卖票 , 直到票被卖完为止 , 所以使用循环语句 , 每循环一次卖一张票 , 打印卖票信息并且将ticket减一 。
⑤创建MyRunnable对象 。
⑥创建三个线程 。
将MyRunnable对象初始化赋值给它 , 并且给各个窗口命名 。
⑦启动线程 。
根据我们前几天学习的线程 , 我们可以写出这样的代码来实现卖票的需求 。
现在看看打印结果:
刘小爱v 自学Java第33天:用一个案例向你说明什么是线程同步?
文章图片
咦 , 发现结果怎么和我想象的不一样?
票确实是在卖票 , 但怎么会是无序的呢?并且有时还会出现重复票 。
【刘小爱v 自学Java第33天:用一个案例向你说明什么是线程同步?】这是为什么?
因为Java虚拟机的抢占式调度 , 我窗口壹先进来了 , 但是我还没有执行完 , 就切换到窗口贰了 。
run方法中的while循环语句执行也是需要时间的 , 虽然它执行起来很快 , 需要的时间也很少 , 但是线程切换更加地快 。
于是就会出现:窗口贰还在打印第6张票 , 窗口叁连续卖了好几张都卖完了这种情况 。
当我们使用多个线程访问同一资源的时候 , 且多个线程对该资源都有操作 , 就容易出现线程安全问题 。
什么叫线程安全问题?
通俗点理解就是:线程一在执行任务的时候 , 还没有执行完 , 线程二也进来执行线程一还没执行完的任务 , 这就乱套了呀 , 这种情况就是线程不安全 。
要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与票无序的问题 , Java中提供了同步机制(synchronized)来解决
二、同步代码块
用synchronized这个关键字来修饰代码块:
刘小爱v 自学Java第33天:用一个案例向你说明什么是线程同步?
文章图片
①Object是Java里的顶层父类 , 它代表了任意对象 。
②同步代码块:锁住了lock这个对象 , 里面的代码就只允许执行一个线程的代码 。
什么意思呢?
就是现在进来了一个线程 , 只要你进入了我锁住的这块代码 , 你就必须得将我锁住的这块代码执行完 。 在执行完之前 , 别的线程根本就进不来 。
其中lock可以是任意对象 , 但要保证它的唯一性 。
这又是什么意思呢?
lock就好比是一把锁 , 线程一、线程二 , 无论哪个线程进来 , 面对的lock都是同一把锁 , 一次就只能进入一个 。
如果我们将①里面创建Object对象的代码放入到run方法里面 , 这样就会出现一个问题:
线程启动就会执行run方法 , 这样的话启动三个线程就会执行三次run方法 , 如果①在run方法里面 , 就会new三个Object对象 , 也就是三个不同的lock , 这样的话就相当于有三把不同的锁 , 还是会乱序 。
这就是lock要保证它的唯一性的原因 。


推荐阅读