🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
**案例1:没有实现线程同步的火车站购票系统** ```java public class SaleWindow implements Runnable { //假定总票数为10张 private int num = 10; @Override public void run() { for (int i = 0; i < 10; i++) { if (num > 0) { System.out.println(Thread.currentThread().getName() + "买处出了第" + num + "张票."); num--; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { SaleWindow window = new SaleWindow(); //创建两个线程,表示正有两个窗口在售票 Thread w1 = new Thread(window); Thread w2 = new Thread(window); w1.setName("window1"); w2.setName("window2"); w1.start(); w2.start(); } } ``` 运行结果如下: ``` window1买处出了第10张票. window2买处出了第10张票. window2买处出了第8张票. window1买处出了第8张票. window1买处出了第6张票. window2买处出了第5张票. window2买处出了第4张票. window1买处出了第4张票. window2买处出了第2张票. window1买处出了第2张票. ``` 可以看到这是不合理的,怎么能让两个窗口卖出的是同一张票。 <br/> 这是因为两条线程彼此独立,但使用相同的资源,不能做到同步更新票数。一条线程操作到一半,cpu被另一条线程抢占,就会出现这个问题,导致线程不安全。 <br/> 如何解决?Java提供了<mark>同步机制(锁)</mark>来解决资源共享问题。让操作共享数据的那段代码,同时只能被一个线程执行(锁住),执行过程中,其它线程不能参与进来。 <br/> Java为每个对象都自动内置了一个锁,某一个线程执行到某个代码片段时就会自动得到这个对象的锁,所以sync(同步)的实现其实就是声明使用这个对象的锁。 <br/> 实现线程同步的两种基本方式:`synchronized`同步代码块、`synchronized`同步方法。 <br/> **案例2:实现了线程同步的火车站购票系统** 1. 使用`synchronized`同步代码块实现线程同步。 ```java public class SaleWindowSyncBlok implements Runnable { //假定总票数为10张 private int num = 10; /** * 使用synchronized同步代码块实现线程同步 */ @Override public void run() { for (int i = 0; i < 10; i++) { //对共享的资源添加同步代码块 synchronized (this) { if (num > 0) { System.out.println(Thread.currentThread().getName() + "买处出了第" + num + "张票."); num--; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { SaleWindowSyncBlok window = new SaleWindowSyncBlok(); //创建两个线程,表示正有两个窗口在售票 Thread w1 = new Thread(window); Thread w2 = new Thread(window); w1.setName("window1"); w2.setName("window2"); w1.start(); w2.start(); } } ``` 2. 或者使用`synchronized`同步方法实现线程同步 ```java public class SaleWindowSyncMethod implements Runnable { //假定总票数为10张 private int num = 10; @Override public void run() { for (int i = 0; i < 10; i++) { salePacket(); } } /** * 或者使用synchronized同步方法实现线程同步 */ public synchronized void salePacket() { if (num > 0) { System.out.println(Thread.currentThread().getName() + "买处出了第" + num + "张票."); } num--; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { SaleWindowSyncMethod window = new SaleWindowSyncMethod(); //创建两个线程,表示正有两个窗口在售票 Thread w1 = new Thread(window); Thread w2 = new Thread(window); w1.setName("window1"); w2.setName("window2"); w1.start(); w2.start(); } } ``` 以上两种的结果都如下一样: ``` window1买处出了第10张票. window1买处出了第9张票. window1买处出了第8张票. window2买处出了第7张票. window2买处出了第6张票. window2买处出了第5张票. window2买处出了第4张票. window2买处出了第3张票. window2买处出了第2张票. window2买处出了第1张票. ``` 这就合理了。这样虽然可以保证了线程的安全,但是会损失性能。