线程安全性问题
来源:互联网 发布:阿里域名优惠口令 编辑:程序博客网 时间:2024/06/01 19:07
线程安全性问题
线程安全性问题说白了就是多个线程在抢夺同一个资源的时候,出现数据不一致的现象。我们来模拟一个售票的情景。
Ticket代码
package com.hy.thread.t4;public class Ticket { private Integer count; public Ticket(Integer count) { this.count = count; } public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } public Integer sale() { if(count >= 1) { System.out.println(Thread.currentThread().getName() + " 卖出第 " + count + " 张票!"); return count --; } return -1; }}
Windows代码
package com.hy.thread.t4;/** * 模拟售票大厅 * * @author 007 * */public class Windows implements Runnable { private Ticket ticket; public Windows(Ticket ticket) { this.ticket = ticket; } @Override public void run() { saleTicket(); } private void saleTicket() { while(ticket.sale() > 0) { } }}
运行类
package com.hy.thread.t4;import java.util.concurrent.atomic.AtomicInteger;public class ThreadDemo { public static void main(String[] args) { Ticket ticket = new Ticket(100); Windows w1 = new Windows(ticket); Thread t1 = new Thread(w1); Thread t2 = new Thread(w1); Thread t3 = new Thread(w1); Thread t4 = new Thread(w1); t1.start(); t2.start(); t3.start(); t4.start(); }}
运行结果如下(截取部分):
Thread-2 卖出第 100 张票!Thread-0 卖出第 100 张票!Thread-3 卖出第 100 张票!Thread-1 卖出第 100 张票!Thread-3 卖出第 97 张票!Thread-0 卖出第 98 张票!Thread-3 卖出第 4 张票!Thread-1 卖出第 5 张票!Thread-0 卖出第 1 张票!Thread-2 卖出第 2 张票!
从运行结果中我们可以看到,同一张票可能会被销售多次,而且甚至可能会出现卖出负数票的现象,这和实际的场景是不符合的,这就出现了线程安全性问题。出现问题的原因就是由于多个线程一起来对tickets进行减操作。而tickets– 这一个操作是一个非原子性操作,因此出现了tickets数据的不一致,也就导致了一张票卖多次的情况。在解决这个问题之前,我们先来搞清楚出现线程安全性问题的原因。通过上面我们可以总结以下三点:
- 首先要有多个线程
- 其次要有共享资源
- 还要对资源进行非原子性操作
以上三点必须同时全部满足才会出现线程安全性问题。缺少其中的任何一个都不会发生线程安全问题。以卖票的例子说明,首先我们创建了4个线程来进行同时卖票,票作为多个线程的共享资源,对票的减操作为非原子性操作。因此出现了线程安全性问题。
这里多次提到了原子操作。那么什么是原子操作呢,我们认为原子是不可再分割的最小单元。那么代码的原子操作也就是说这段代码一步就执行完毕了,不会分成多步来执行。比如我们上面遇到的tickets– 这个就不是原子性操作,为什么呢,因为tickets– 可以认为是先 减一,再赋值两步操作。
分析出了问题的原因,那么我们改如何解决这个问题呢?
- 多线程改为单线程
- 去掉共享资源
- 将非原子性操作转变为原子性操作
以上三点我们发现出了第三点可以为我们所用以外,以上两点是以程序为代价的,一般来讲是不会使用的。那么我们所研究的重点也就到了把非原子性操作转为原子性操作。
如何把非原子性操作转为原子性操作呢,有很多的手段,这也是我们后面的内容要详细的来学习的。这里仅仅做一个简单的描述。首先就是我们所熟悉的同步监视器,也就是在出现线程安全问题的方法上添加synchronized关键字,这样标识有synchronized关键字的方法在同一时刻只能有一个线程进入,必须等待一个线程执行完毕其他线程才能开始执行,也就相当于原子性操作。另外可以使用jdk提供的原子操作,比如把Integer换成AtomicInteger,AtomicInteger就是一个原子类,那么把减操作就可以使用其提供的方法来处理。另外jdk也给我们提供了大量的锁,我们也可以使用各种的锁来保证方法的原子性操作。
1.使用synchronized关键字来解决线程安全性问题
public synchronized Integer sale() { if(count >= 1) { System.out.println(Thread.currentThread().getName() + " 卖出第 " + count + " 张票!"); return count --; } return -1;}
结果如下
Thread-0 卖出第 20 张票!Thread-2 卖出第 19 张票!Thread-2 卖出第 18 张票!Thread-2 卖出第 17 张票!Thread-2 卖出第 16 张票!Thread-2 卖出第 15 张票!Thread-2 卖出第 14 张票!Thread-2 卖出第 13 张票!Thread-2 卖出第 12 张票!Thread-2 卖出第 11 张票!Thread-2 卖出第 10 张票!Thread-2 卖出第 9 张票!Thread-2 卖出第 8 张票!Thread-2 卖出第 7 张票!Thread-2 卖出第 6 张票!Thread-2 卖出第 5 张票!Thread-2 卖出第 4 张票!Thread-2 卖出第 3 张票!Thread-3 卖出第 2 张票!Thread-1 卖出第 1 张票!
使用synchronized是如何保证线程安全性问题的呢?
当一个线程试图访问被synchronized修饰的方法时,它首先必须得到锁。退出或者抛出异常时必须释放锁。那么当线程进来之后会拿到锁,别的线程执行到这个方法之后就会等待,直到拿到锁的线程执行完毕之后,将锁释放,另一个线程才能进入。因此在同一时刻,被synchronized修饰的方法只能被同一个线程执行,这也就保证了线程执行的安全性。
- 使用AtomicInteger来代替Integer
package com.hy.thread.t4;import java.util.concurrent.atomic.AtomicInteger;public class Ticket { private AtomicInteger count; public Ticket(Integer count) { this.count = new AtomicInteger(count); } public AtomicInteger getCount() { return count; } public void setCount(AtomicInteger count) { this.count = count; } public Integer sale() { return count.getAndDecrement(); // 注意这里 }}
处理线程安全性问题是在多线程编程中需要时刻注意的问题,多线程的复杂之处其实也就在于性能与安全性之间的平衡。我们可以直接通过synchronized来解决线程安全问题,但性能却大大的降低。
- Servlet线程安全性问题
- Servlet线程安全性问题
- Servlet线程安全性问题
- Servlet线程安全性问题
- Servlet线程安全性问题
- Servlet线程安全性问题
- 线程安全性问题
- Servlet线程安全性问题
- servlet线程安全性问题
- Servlet线程安全性问题
- Servlet线程安全性问题
- 线程安全性问题
- 线程安全性问题
- Servlet线程安全性问题
- 线程安全性问题
- Servle t线程安全性问题
- IConfigurationSectionHandler的线程安全性问题
- servlet线程安全性问题理解
- STM32-UCOSIII学习笔记3
- UVA11475 KMP算法简单运用
- Android简易计算器
- 如何将使用托管磁盘虚拟机的 OS 盘挂载到其他虚拟机上
- megaface使用
- 线程安全性问题
- android studio 重写类的方法
- C 运算符
- iOS 多线程的四种技术方案 OC
- java中ajax实现文件上传下载
- IL语言如何在.NET下运行?
- Bootstrap中的轮播
- Java虚拟机:Java垃圾回收(GC)机制详解
- SSL证书对电商平台的重要性