- 线程安全:数据共享时对数据读写出现错误(数据污染)。
- 出现线程安全条件:
a.多线程环境下,单线程无线程安全问题。
b.数据共享发生,无数据共享无线程安全问题。
c.对数据读写时,单一读无线程安全问题(数据共享),单一写存在线程问题。
经典售票问题
描述:现有100张(序号1-100)动物园门票出售。现有三个售票窗口进行售票。编写一个多线程程序模拟窗口售票。
分析:
- 三个窗口出售不同的票。没有什么问题,不存在数据共享不存在线程安全问题。
2.三个窗口同时出售100张票。这时候就会出现问题。数据共享读写出现线程问题。
Java编写程序:
按照分析第二条进行代码编写测试
运行程序控制台打印出现数据污染:
错误分析:先理解下线程调度的方式:线程的调度方式为抢占式,多个线程抢占资源,抢到资源处于运行状态,没抢到进入阻塞状态。运行线程释放资源,线程继续抢占。如此循环保证只有一个线程处于运行状态。因为线程调度和释放资源的速度很快所以感觉到多个线程同时在执行。
右下图分析可以看出:当一个线程执行到打印(读数据)时失去执行权,并没有对票数进行tiicket--操作,其他线程也进入运行状态这个时候就会出现多个窗口出售相同票的问题。执行到最后一张票时,可能还会出现线程执行减操作,其他线程进行读取时出现负数票情况。
线程安全解决方案
采用线程同步原理:使可能出现线程安全问题的代码始终只能有一个在执行,并且执行完毕后其他线程才能执行其中代码。
-
同步代码块:
synchronized(锁对象){
//可能出现线程安全的代码
}
2.同步方法:
对可能出现线程安全的代码使用同步方法
修饰词 synchronized 返回值 方法名(参数){
//可能出现线程安全的代码
}
3.使用Lock锁(java.util.concurrent.locks.Lock):
a.创建锁对象(新建Lock接口的实现类 例如:Lock lock = new ReentrantLock(); )
b.在可能出现线程安全的代码前获取锁
c.在可能出现线程安全的代码后释放锁