1 package day2_4; 2 3 /** 4 *例子:创建三个窗口卖票,总票数为100张,使用实现Runnable接口 5 * 1.问题:卖票过程中,出现了重票、错票的情况 --->出现了线程安全问题 6 * 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票 7 * 3.如何解决:当一个线程A在操作ticket时,其他线程不能参与进来,直到线程A操作完(即使A出现阻塞),其他线程才可以开始 8 * 才做操作ticket 9 * 4.在Java中我们通过同步机制,来解决线程的安全问题 10 * 11 * 方式一:同步代码块 12 * 13 * synchronized(同步监视器){ 14 * //需要被同步的代码 15 * } 16 * 17 * 说明:1.操作共享数据的代码,就是需要被同步的代码 -->不能包含代码少了,也不能包含代码多了(1.可以会改变逻辑 2.效率变低) 18 * 2.共享数据:多个线程共同操作的变量,比如ticket就是共享数据 19 * 3.同步监视器:俗称锁。任何一个类的对象都可以充当锁 20 * 要求:多个线程必须共用同一把锁(也就是多个线程共享的一个对象) 21 * 22 * 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this来充当同步监视器 23 * 说明:但是在继承Thread类创建多线程的方式中,慎用this充当同步监视器,可以考虑使用 24 * 线程类的类对象充当同步监视器 25 * 26 * 方式二:同步方法 27 * 28 * 29 * 5.同步的方式:解决了线程的安全问题 -->好处 30 * 操作同步代码块时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低 (局限性) 31 * 32 * 33 * @Author Tianhao 34 * @create 2021-02-04-18:20 35 */ 36 37 38 class Window3 implements Runnable { 39 40 private int ticket = 100; 41 42 Object obj = new Object(); 43 44 @Override 45 public void run() { 46 while (true) { 47 //下面两行都是可以的 48 //synchronized(obj){ //obj就是多个线程都共享的对象 49 synchronized(this){ //this表示 执行run()方法所在的Window3类的对象w,是多个线程共享的对象 50 51 //while (true) { //包多了,就会一个线程全部把票卖完了,逻辑错了! 52 if (ticket > 0) { 53 System.out.println(Thread.currentThread().getName() + "卖票,票号:" 54 + ticket); 55 56 //每个线程执行到这里都会阻塞100毫秒 57 try { 58 Thread.sleep(100); 59 } catch (InterruptedException e) { 60 e.printStackTrace(); 61 } 62 63 ticket--; 64 } 65 } 66 } 67 } 68 } 69 70 public class WindowTest3 { 71 72 public static void main(String[] args) { 73 Window3 w = new Window3(); 74 Thread t1 = new Thread(w); 75 Thread t2 = new Thread(w); 76 Thread t3 = new Thread(w); 77 t1.setName("窗口1"); 78 t2.setName("窗口2"); 79 t3.setName("窗口3"); 80 t1.start(); 81 t2.start(); 82 t3.start(); 83 } 84 }