Java学习-078-多线程11:使用 synchronized 同步代码块解决线程资源同步问题
2023-09-11 14:18:59 时间
多线程资源同步问题,可通过 synchronized (同步代码块)来解决,同步代码块使用方法如下所示:
synchronized (同步对象) { // 同步代码块,对同步对象共享资源的操作 }
将操作线程同一共享资源的代码作为同步代码块包含在 synchronized 同步代码块中,即可解决多线程的资源同步问题。具体实例源代码如下所示:
package com.fanfengping.demo; import lombok.extern.slf4j.Slf4j; @Slf4j public class Demo02SaleTicketRunnable implements Runnable { private int stockTicket; private int saleTicket; Demo02SaleTicketRunnable() { stockTicket = 20; saleTicket = 0; } @Override public void run() { log.info("Running Runnable Thread : {}", Thread.currentThread().getName()); while(stockTicket > 0) { synchronized (this) { try { if (stockTicket > 0) { log.info("Runnable {}:已售票数 {},剩余票数 {}{}", Thread.currentThread().getName(), ++saleTicket, --stockTicket, stockTicket == 0 ? ",停止售票!" : ""); } else { log.info("Runnable {}:已售票数 {},剩余票数 {},停止售票!", Thread.currentThread().getName(), saleTicket, stockTicket); break; } // 同步代码语句块里休眠,有点类似不休眠,此时sleep相当于抱着锁休眠 // 当sleep不释放锁时,自身不执行,线程也不执行,同时其他线程也无法获得锁,也不能执行同步代码 Thread.sleep(800); } catch (InterruptedException e) { log.info("Runnable {} interrupted!", Thread.currentThread().getName()); e.printStackTrace(); } } } log.info("Runnable {} exit", Thread.currentThread().getName()); } }
测试源代码如下所示:
package com.fanfengping.demo; public class Demo02SaleTicketRunnableTest { public static void main(String[] args) { Demo02SaleTicketRunnable st = new Demo02SaleTicketRunnable(); for (int j = 1; j < 6; j++) { new Thread(st, "售票窗口 " + j).start(); } } }
执行程序输出结果如下所示:
[售票窗口 5] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 5 [售票窗口 3] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 3 [售票窗口 2] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 2 [售票窗口 1] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 1 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 4 [售票窗口 5] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 5:已售票数 1,剩余票数 19 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 2,剩余票数 18 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 3,剩余票数 17 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 4,剩余票数 16 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 5,剩余票数 15 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 6,剩余票数 14 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 7,剩余票数 13 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 8,剩余票数 12 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 9,剩余票数 11 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 10,剩余票数 10 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 11,剩余票数 9 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 12,剩余票数 8 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 13,剩余票数 7 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 14,剩余票数 6 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 15,剩余票数 5 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 16,剩余票数 4 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 17,剩余票数 3 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 18,剩余票数 2 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 19,剩余票数 1 [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 20,剩余票数 0,停止售票! [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4 exit [售票窗口 1] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 1:已售票数 20,剩余票数 0,停止售票! [售票窗口 1] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 1 exit [售票窗口 2] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 2:已售票数 20,剩余票数 0,停止售票! [售票窗口 2] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 2 exit [售票窗口 3] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 3:已售票数 20,剩余票数 0,停止售票! [售票窗口 3] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 3 exit [售票窗口 5] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 5:已售票数 20,剩余票数 0,停止售票! [售票窗口 5] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 5 exit
根据程序输出可以看出,虽然售票信息正确,未出现数据不一致的情况,但各个售票窗口的售票操作较为集中在 售票窗口 4,并非离散型的销售。
因为 Thread.sleep 也在同步代码块中,在同步代码块中进行休眠时,实际线程依然占用着同步锁,并未释放,其他活动的线程无法获取同步锁自然也无法执行。将休眠放到同步锁外面即可解决,源代码示例如下所示:
package com.fanfengping.demo; import lombok.extern.slf4j.Slf4j; @Slf4j public class Demo03SaleTicketRunnable implements Runnable { private int stockTicket; private int saleTicket; Demo03SaleTicketRunnable() { stockTicket = 20; saleTicket = 0; } @Override public void run() { log.info("Running Runnable Thread : {}", Thread.currentThread().getName()); while(stockTicket > 0) { synchronized (this) { if (stockTicket > 0) { log.info("Runnable {}:已售票数 {},剩余票数 {}{}", Thread.currentThread().getName(), ++saleTicket, --stockTicket, stockTicket == 0 ? ",停止售票!" : ""); } else { log.info("Runnable {}:已售票数 {},剩余票数 {},停止售票!", Thread.currentThread().getName(), saleTicket, stockTicket); break; } } // 将 sleep 放在同步代码块的外面,防止休眠时占用同步锁 try { Thread.sleep(800); } catch (InterruptedException e) { log.info("Runnable {} interrupted!", Thread.currentThread().getName()); e.printStackTrace(); } } log.info("Runnable {} exit", Thread.currentThread().getName()); } }
测试源代码如下所示:
package com.fanfengping.demo; public class Demo03SaleTicketRunnableTest { public static void main(String[] args) { Demo03SaleTicketRunnable st = new Demo03SaleTicketRunnable(); for (int j = 1; j < 6; j++) { new Thread(st, "售票窗口 " + j).start(); } } }
执行程序输出结果如下所示:
[售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 4 [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 2 [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 3 [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 1 [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 5 [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4:已售票数 1,剩余票数 19 [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5:已售票数 2,剩余票数 18 [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1:已售票数 3,剩余票数 17 [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3:已售票数 4,剩余票数 16 [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2:已售票数 5,剩余票数 15 [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5:已售票数 6,剩余票数 14 [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3:已售票数 7,剩余票数 13 [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2:已售票数 8,剩余票数 12 [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4:已售票数 9,剩余票数 11 [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1:已售票数 10,剩余票数 10 [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1:已售票数 11,剩余票数 9 [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4:已售票数 12,剩余票数 8 [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3:已售票数 13,剩余票数 7 [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2:已售票数 14,剩余票数 6 [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5:已售票数 15,剩余票数 5 [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5:已售票数 16,剩余票数 4 [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1:已售票数 17,剩余票数 3 [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4:已售票数 18,剩余票数 2 [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2:已售票数 19,剩余票数 1 [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3:已售票数 20,剩余票数 0,停止售票! [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3 exit [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1 exit [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5 exit [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4 exit [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2 exit
当然也可将同步的代码块单独放到一个方法中,然后在 run 方法中调用,源代码示例如下所示:
package com.fanfengping.demo; import lombok.extern.slf4j.Slf4j; @Slf4j public class Demo04SaleTicketRunnable implements Runnable { private int stockTicket; private int saleTicket; Demo04SaleTicketRunnable() { stockTicket = 20; saleTicket = 0; } private void sellTicket() { synchronized (this) { if (stockTicket > 0) { log.info("Runnable {}:已售票数 {},剩余票数 {}{}", Thread.currentThread().getName(), ++saleTicket, --stockTicket, stockTicket == 0 ? ",停止售票!" : ""); } else { log.info("Runnable {}:已售票数 {},剩余票数 {},停止售票!", Thread.currentThread().getName(), saleTicket, stockTicket); } } } @Override public void run() { log.info("Running Runnable Thread : {}", Thread.currentThread().getName()); while(stockTicket > 0) { sellTicket(); try { // 将 sleep 放在同步代码块外,在售票完成后即释放同步锁,并休眠一会,允许其他窗口继续售票 Thread.sleep(800); } catch (InterruptedException e) { e.printStackTrace(); } } log.info("Runnable {} exit", Thread.currentThread().getName()); } }
测试源代码如下所示:
package com.fanfengping.demo; public class Demo04SaleTicketRunnableTest { public static void main(String[] args) { Demo04SaleTicketRunnable st = new Demo04SaleTicketRunnable(); for (int j = 1; j < 6; j++) { new Thread(st, "售票窗口 " + j).start(); } } }
执行程序输出结果如下所示:
[售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 2 [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 1 [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 5 [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 3 [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2:已售票数 1,剩余票数 19 [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 4 [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3:已售票数 2,剩余票数 18 [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4:已售票数 3,剩余票数 17 [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5:已售票数 4,剩余票数 16 [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1:已售票数 5,剩余票数 15 [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3:已售票数 6,剩余票数 14 [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4:已售票数 7,剩余票数 13 [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2:已售票数 8,剩余票数 12 [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1:已售票数 9,剩余票数 11 [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5:已售票数 10,剩余票数 10 [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2:已售票数 11,剩余票数 9 [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4:已售票数 12,剩余票数 8 [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3:已售票数 13,剩余票数 7 [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5:已售票数 14,剩余票数 6 [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1:已售票数 15,剩余票数 5 [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2:已售票数 16,剩余票数 4 [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3:已售票数 17,剩余票数 3 [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4:已售票数 18,剩余票数 2 [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5:已售票数 19,剩余票数 1 [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1:已售票数 20,剩余票数 0,停止售票! [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2 exit [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3 exit [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4 exit [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1 exit [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5 exit
相关文章
- Java 并发工具包 java.util.concurrent 用户指南
- java高级用法之:在JNA中将本地方法映射到JAVA代码中
- Java 迭代器 Iterator
- Java多线程之线程同步
- java 多线程并发 synchronized 同步机制及方式
- 利用Java中的现有方法实现对集合元素进行排序
- 监视器–JAVA同步基本概念
- 【Java】java基本知识
- 【Java】java: 无法访问org.testng.annotations.Test
- java 项目 文件关系 扫描 注释注入(2)
- java程序员成长的几大成长法则
- Java线程:线程的同步与锁
- java 反射实现框架功能
- Java 8 Stream api 入门
- Java线程锁,synchronized、wait、notify详解--java 管程
- elasticsearch之JAVA环境变量报错:could not find java; set JAVA_HOME or ensure java is in PATH
- java高级用法之:在JNA中将本地方法映射到JAVA代码中
- Java多线程-线程的同步与锁
- 『Java练习生的自我修养』java-se进阶¹ • 初识多线程
- Java中的异常
- JAVA基础(1 集合框架)
- 阶乘运算 (java,c 同步运算)
- How to improve Java's I/O performance( 提升 java i/o 性能)
- Java Swing编程接口(30)---列表框:JList
- Java线程同步
- java idea 创建第一个java 程序
- Java多线程-synchronized同步方法及同步块简述
- Java中java.util.Arrays参考指南
- Java UDP 单播、多播(组播)、广播、任播(未实现)