线程安全问题的概述与线程安全问题的代码实现
线程安全
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
我们通过一个案例,演示线程的安全问题:
电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是“葫芦娃大战奥特曼”,本次电影的座位共100个(本场电影只能卖100张票)。
我们来模拟电影院的售票窗口,实现多个窗口同时卖“葫芦娃大战奥特曼"这场电影票(多个窗口一起卖这100张票)需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟
二、简介
1.什么是线程安全?
(1)控制多个线程对某个资源的有序访问或修改
(2)如果一个类在多线程的访问下,其状态是可以 预测的,并且不需要额外的同步,那么这个类 就是线程安全的类
2. 为什么要线程安全?
(1) 线程不安全会造成数据错误,修正数据错误是 费时费力的
(2) 线程不安全会导致程序发生一些异常行为,而 且这种行为很难查找
(3) 线程不安全的现象一般只有在并发量大时才会 出现,因此很难重现
三、 线程不安全产生原因
1.线程工作内存与主存交互
当线程操作某个共享变量时,可能的执行顺序如下:
(1)从主存复制变量到当前工作内存
(2)执行代码,改变共享变量的值
(3) 将工作内存数据同步回主存
2. 内存可见性与互斥性
(1)可见性:一个线程的执行结果可以被另一个线程 所看到
(2)互斥性:在同一时间只能有一个线程对变量的主 存进行操作
package Demo01_Sleep; /* 实现卖票案例 */ public class RunnableImpl implements Runnable{ // 定义一个多线程贡献的票源 private int ticket = 100; // 设置线程任务:卖票 @Override public void run() { // 使用死循环 让卖票操作重复执行 while (true){ // 先判断票是否存在 if (ticket >= 0){ // 使用线程睡眠提高安全问题出现的概率 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // 卖票 System.out.println("在卖"+Thread.currentThread().getName()+"线程的第:"+ticket+"张票"); ticket--; } } } } /* 模拟卖票案例 创建了3个共享线程,同时开启,对共享的票进行出售 */ class Demo01Ticket { public static void main(String[] args) { // 创建实现类对象 RunnableImpl runnable = new RunnableImpl(); // 创建Thread类对象,构造方法中传递Runnable接口的实现类 Thread thread0 = new Thread(runnable); Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable); // 调用start开启多线程 thread0.start(); thread1.start(); thread2.start(); } }
package Demo01_Sleep; public class SellTicket implements Runnable { private int tickets = 100; //在SellTicket类中重写run()方法实现卖票,代码步骤如下 @Override public void run() { while (true) { if (tickets > 0) { System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } } } } class SellTicketDemo { public static void main(String[] args) { //创建SellTicket类的对象 SellTicket st = new SellTicket(); //创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称 Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); //启动线程 t1.start(); t2.start(); t3.start(); } }