一、前言
在学习同步机制之前,我们先来看一个买票的案例,这样有助于我们的理解。
二、版本一
因为实现Runnable接口的方式更能满足我们的需求,而且还很便利。所以本文的所有案例都是使用该种方式。
1 | public class SellTicket implements Runnable { |
1 | public class SellTicketDemo { |
此时的代码已经基本满足要求了,但是由于在实际情况下,买票是存在延迟的,所以我们添加代码,模拟延迟。
三、版本二
(一)修改后的代码
1 | public class SellTicket implements Runnable { |
测试代码同上。
(二)出现的问题
当我们运行时,会发现存在以下问题:
1、相同的票卖了多次
2、出现了负数票
(三)问题出现的原因
那出现这两个问题的原因是什么呢?
1)相同的票卖了多次:CPU的一次操作必须是原子性的
2)出现了负数票:随机性和延迟导致的
(四)对于这两个问题的详细解释
相同的票卖了多次
因为CPU的每一次执行必须是一个原子性(最简单基本的)的操作。而我们代码中的 tickets– 不是一个原子性的操作。这是由两步组成的。因此,在输出了当前tickets值,但是还没有–的时候,被另外的进行抢到了CPU的执行权。所以输出了两个相同的票数。
出现了负数票
当我们的tickets的值为1时,假设有一个线程A抢到了执行权,但是线程A执行到了sleep(100)时,就会变成就绪状态,失去执行权。此时,执行权被另外线程B拿到,然后线程B也会进行继续状态。这个时候,线程A的sleep时间差不多到了,因此就可能抢到了执行权,也就能继续往下执行,所以输出第1张票,然后修改票数为0。但是,由于线程B也睡眠完毕,因此也能继续执行,所以,修改票数为负数。
在案例代码中的情况下,最坏的状态为票数被修改至-1。因为可能在线程A睡眠的时候,票数还是1,这时抢到执行权的线程B就能通过if判断。而线程A睡眠完成之后,可能没有抢到执行权,被线程C抢占到了,但是线程B还在睡眠,也没有修改票数。所以,此时线程C也能通过if判断。所以,在一个线程执行完,修改票数至1之后,另外两个线程依然能够继续执行,分别修改票数为0和-1。
四、版本三
其实上述问题就是多线安全问题,那么我们如何解决呢?
(一)线程安全问题的产生原因
这些原因也是以后我们判断一个程序是否会有线程安全问题的标准。
1)是否是多线程环境
2)是否有共享数据
3)是否有多条语句操作共享数据
(二)线程安全问题的解决
思想:把多条操作共享数据的代码给包成一个整体,让某个线程在执行的时候,其它线程不能来执行。
(三)同步机制
前面已经提到我们的解决思路就是将多条操作共享数据的代码给包成一个整体。
因此,Java就给我们提供了同步机制。
我们可以使用同步代码块来将需要同步的代码组成一个整体,如下:
同步代码块格式:
1 | synchronized(锁对象){ |
1 | public class SellTicket implements Runnable { |
1 | public class SellTicketDemo { |
注意事项:
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能;
多个线程必须是同一把锁。
使用同步代码块后,前面提到的两个问题就已经能够解决了,但同步代码块只是同步机制的一种形式。
接下来让我们仔细的了解下Java的同步机制。
五、同步机制
(一)分类
同步代码块、同步方法、静态同步方法
(二)特点
同步的前提
多个线程
多个线程使用的是同一个锁对象
同步的好处
同步的出现解决了多线程的安全问题
同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率;
容易产生死锁
(三)同步方法
同步方法就是使用synchronized关键字进行修饰。案例中代码修改成如下:
1 | public class SellTicket implements Runnable { |
(四)静态同步方法
静态同步方法就是使用static和synchronized关键字进行修饰。案例中代码修改成如下:
1 | public class SellTicket implements Runnable { |
(五)锁对象问题
同步代码块的锁对象:任意对象,只需要确保所有进行使用的都是同一个锁对象即可(即:不会每次都重新创建一个对象);
同步方法的锁对象:this对象;
静态同步方法的锁对象:当前类的字节码文件对象。
六、总结
同步机制解决多线程安全问题的关键就是:
1)将操作共享数据的代码作为一个整体,使用锁将其锁起来,有线程正在操作时,其它线程不能操作;
2)确保所有线程使用的是同一个锁对象。
Java新手,若有错误,欢迎指正!