什么是线程安全
所谓线程安全指的是多个线程对同一资源进行访问时,有可能产生数据不一致问题,导致线程访问的资源并不是安全的。
判断线程安全
判断一个程序是否存在线程安全问题的依据
- 是否有多线程环境
- 是否有共享数据
- 是否有多条语句操作共享数据
电影院卖票案例
某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
库存类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
class Stock {
public final static int originalStock = 100;
public static AtomicInteger remainStock = new AtomicInteger(originalStock);
public static int sellTickets = 0;
public static AtomicInteger sellTicketsAtomic = new AtomicInteger(0); }
|
方式一:继承Thread类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class SellTicketDemo { public static void main(String[] args) { SellTicket st1 = new SellTicket(); SellTicket st2 = new SellTicket(); SellTicket st3 = new SellTicket();
st1.setName("窗口1"); st2.setName("窗口2"); st3.setName("窗口3");
st1.start(); st2.start(); st3.start(); } }
class SellTicket extends Thread { @Override public void run() { while (Stock.sellTickets < Stock.originalStock) { try { Thread.sleep(100); System.out.println(getName() + "\t\t正在出售第" + (++Stock.sellTickets) + "张票\t\t" + "原子操作出售第" + Stock.sellTicketsAtomic.incrementAndGet() + "张票\t\t" + "原始库存" + Stock.originalStock + "张票\t\t" + "库存剩余" + Stock.remainStock.decrementAndGet() + "张票\t\t"); } catch (InterruptedException e) { e.printStackTrace(); } }
System.out.println("库存剩余" + Stock.remainStock.get()); } }
|
方式二:实现Runnable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class SellTicketDemo { public static void main(String[] args) { SellTicket st = new 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(); } }
class SellTicket extends Stock implements Runnable { @Override public void run() { while (sellTickets < originalStock) { try { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + "\t\t正在出售第" + (++sellTickets) + "张票\t\t" + "原子操作出售第" + sellTicketsAtomic.incrementAndGet() + "张票\t\t" + "原始库存" + originalStock + "张票\t\t" + "库存剩余" + remainStock.decrementAndGet() + "张票\t\t"); } catch (InterruptedException e) { e.printStackTrace(); } }
System.out.println("库存剩余" + remainStock.get()); } }
|
发现问题(线程不安全)
为了更符合真实的场景,加入了休眠100毫秒,导致: