多线程并发操作同一数据时, 就有可能出现线程安全问题
使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 同步后,就不会有多个线程同时执行这段代码了。
需求
动物园售票,一共100张票,通过四个窗口同时售卖,卖完停止。
使用继承Thread实现
/*** 售票窗口*/class TicketWindow extends Thread {/*** 总票数*/private static int ticket = 100;/*** 窗口名称*/private String windowName;//private static Object lock = new Object(); //如果用引用数据类型成员变量当作锁对象,必须是静态的,否则,每次创建当前对象都是一个新的对象public TicketWindow(String windowName) {super(windowName);this.windowName = windowName;}@Overridepublic void run() {//开票卖票doSale();}public void doSale(){while(true) {synchronized(TicketWindow.class) { //如果使用this,不会起到作用,因为是4个TicketWindow对象卖票,不是同一个对象if(ticket <= 0) {//停止卖票break;}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName() + "卖掉第 " + ticket-- + " 号票");}}}}
public static void main(String[] args) {/*** 4个窗口卖票*/new TicketWindow("1号窗口").start();new TicketWindow("2号窗口").start();new TicketWindow("3号窗口").start();new TicketWindow("4号窗口").start();}
..................1号窗口卖掉第 4 号票4号窗口卖掉第 4 号票2号窗口卖掉第 4 号票2号窗口卖掉第 3 号票4号窗口卖掉第 3 号票3号窗口卖掉第 3 号票1号窗口卖掉第 3 号票2号窗口卖掉第 2 号票4号窗口卖掉第 0 号票1号窗口卖掉第 1 号票3号窗口卖掉第 2 号票
观察上面输出结果,发现同一张票会被多个窗口多次卖出。
1号窗口卖掉第 100 号票1号窗口卖掉第 99 号票4号窗口卖掉第 98 号票4号窗口卖掉第 97 号票3号窗口卖掉第 96 号票3号窗口卖掉第 95 号票3号窗口卖掉第 94 号票3号窗口卖掉第 93 号票3号窗口卖掉第 92 号票3号窗口卖掉第 91 号票3号窗口卖掉第 90 号票3号窗口卖掉第 89 号票3号窗口卖掉第 88 号票3号窗口卖掉第 87 号票3号窗口卖掉第 86 号票3号窗口卖掉第 85 号票3号窗口卖掉第 84 号票2号窗口卖掉第 83 号票2号窗口卖掉第 82 号票2号窗口卖掉第 81 号票3号窗口卖掉第 80 号票4号窗口卖掉第 79 号票4号窗口卖掉第 78 号票1号窗口卖掉第 77 号票1号窗口卖掉第 76 号票1号窗口卖掉第 75 号票1号窗口卖掉第 74 号票1号窗口卖掉第 73 号票1号窗口卖掉第 72 号票4号窗口卖掉第 71 号票4号窗口卖掉第 70 号票4号窗口卖掉第 69 号票4号窗口卖掉第 68 号票4号窗口卖掉第 67 号票3号窗口卖掉第 66 号票2号窗口卖掉第 65 号票3号窗口卖掉第 64 号票3号窗口卖掉第 63 号票3号窗口卖掉第 62 号票4号窗口卖掉第 61 号票1号窗口卖掉第 60 号票1号窗口卖掉第 59 号票1号窗口卖掉第 58 号票1号窗口卖掉第 57 号票4号窗口卖掉第 56 号票4号窗口卖掉第 55 号票4号窗口卖掉第 54 号票4号窗口卖掉第 53 号票4号窗口卖掉第 52 号票3号窗口卖掉第 51 号票2号窗口卖掉第 50 号票2号窗口卖掉第 49 号票2号窗口卖掉第 48 号票3号窗口卖掉第 47 号票4号窗口卖掉第 46 号票1号窗口卖掉第 45 号票1号窗口卖掉第 44 号票1号窗口卖掉第 43 号票1号窗口卖掉第 42 号票1号窗口卖掉第 41 号票1号窗口卖掉第 40 号票1号窗口卖掉第 39 号票1号窗口卖掉第 38 号票1号窗口卖掉第 37 号票1号窗口卖掉第 36 号票1号窗口卖掉第 35 号票1号窗口卖掉第 34 号票1号窗口卖掉第 33 号票1号窗口卖掉第 32 号票1号窗口卖掉第 31 号票1号窗口卖掉第 30 号票1号窗口卖掉第 29 号票1号窗口卖掉第 28 号票1号窗口卖掉第 27 号票1号窗口卖掉第 26 号票1号窗口卖掉第 25 号票1号窗口卖掉第 24 号票1号窗口卖掉第 23 号票1号窗口卖掉第 22 号票1号窗口卖掉第 21 号票1号窗口卖掉第 20 号票1号窗口卖掉第 19 号票4号窗口卖掉第 18 号票3号窗口卖掉第 17 号票2号窗口卖掉第 16 号票2号窗口卖掉第 15 号票2号窗口卖掉第 14 号票2号窗口卖掉第 13 号票2号窗口卖掉第 12 号票3号窗口卖掉第 11 号票3号窗口卖掉第 10 号票3号窗口卖掉第 9 号票3号窗口卖掉第 8 号票3号窗口卖掉第 7 号票3号窗口卖掉第 6 号票4号窗口卖掉第 5 号票1号窗口卖掉第 4 号票1号窗口卖掉第 3 号票4号窗口卖掉第 2 号票3号窗口卖掉第 1 号票
使用Runnable接口实现
/*** 需求:* 动物园售票,一共100张票,通过四个窗口售卖,卖完停止。* 使用Runnable接口实现* @author Axin*/public class Demo04SaleTickets {public static void main(String[] args) {//所有的票Ticket tickets = new Ticket();//4个窗口卖票new Thread(tickets,"1号窗口").start();new Thread(tickets,"2号窗口").start();new Thread(tickets,"3号窗口").start();new Thread(tickets,"4号窗口").start();/*** 多次启动线程同一个线程是不合法的。 特别是,一旦线程完成执行不能再重新启动。* 异常* IllegalThreadStateException - 如果线程已经启动。* ---来自JDK文档**///Thread t1 = new Thread(tickets);//t1.start();//t1.start();//t1.start();//t1.start();}}/*** 所有的票*/class Ticket implements Runnable {/*** 总票数*/private int ticket = 100;@Overridepublic void run() {//开票卖票doSale();}public void doSale(){while(true) {synchronized(this) {if(ticket <= 0) {//停止卖票break;}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "卖掉第 " + ticket-- + " 号票");}}}}
