synchronized介绍

一、基本概念

synchronized关键字是java里面用来在多线程环境下保证线程安全的同步锁;java里面有对象锁和类锁,对象锁是用在对象实例的方法上或者一个对象实例上的,而类锁是用在一个类的静态方法上或者一个类的class对象上的。所以对于对象锁,不同的实例对象的对象锁不同,但是类锁只有一个,所有的对象实例共享这个类锁

二、synchronize使用场景

1、修饰类中的普通方法:在类中的普通方法上加上synchronized修饰,锁对象是调用当前同步方法的对象实例,线程在执行该方法时,首先需要拿到该对象锁。
2、修饰类中的静态方法:在类中的静态方法上加上synchronized修饰,锁对象是当前类的Class对象,线程在执行该方法时,首先需要拿到该类锁,一个类不同的对象实例的类锁是同一把锁。
3、修饰代码块:修饰代码块时,传入的锁对象可以是对象实例也可以是类的Class对象,分别对应1和2情况,但是synchronized修饰代码块比上面两中修饰方法有一个优点,就是颗粒度更小,可以只同步我们需要同步的部分代码,其他不需要同步的代码不会被同步。

三、生产者消费者实例

1、生产者:负责不断生产票,当存储票的仓库满了的时候则停止生产票,唤醒消费者消费票

/*** 生成票 .*/public void provideTickets() {synchronized (Tickets.class) {// 不断生产票while (true) {// 唤醒沉睡线程(唤醒消费者)Tickets.class.notify();try {//当前线程睡眠一秒钟,继续持有当前锁Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 票满了 则停止生产if (Tickets.tickets == Tickets.MAXNUMTICHETS) {System.out.println(Thread.currentThread().getName() + "--->仓库满了,不能继续生产票了");try {Tickets.class.wait();} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "try");e.printStackTrace();}}// 生产票 随机生产多张票,不需要一直生产到仓库满,模拟有票就可以卖int num = new Random().nextInt(10);while ((Tickets.tickets < Tickets.MAXNUMTICHETS) && (num >= 0)) {Tickets.tickets++;System.out.println(Thread.currentThread().getName() + "--->生产一张票,还有" + Tickets.tickets + "张票");num--;}try {//生产了多张票则放弃锁,让消费者消费Tickets.class.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}

2、消费者:负责不断消费票,当票被卖完了的时候唤醒生产者生产票。

/*** 卖票 .*/public void saleTickets() {synchronized (Tickets.class) {// 不断买票while (true) {// 唤醒沉睡线程(唤醒生产生产票)Tickets.class.notify();try {//睡眠一秒钟,不释放当前锁Thread.sleep(1000);} catch (InterruptedException e1) {e1.printStackTrace();}// 如果票卖完,则通知生产票if (Tickets.tickets == 0) {System.out.println(Thread.currentThread().getName() + "--->票都没了,还让我们去买,骗子");try {Tickets.class.wait();} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "try");e.printStackTrace();}}// 卖票 随机卖出多张票,不一定要票卖完才通知生产者int num = new Random().nextInt(10);while ((Tickets.tickets > 0) && (num > 0)) {Tickets.tickets--;System.out.println(Thread.currentThread().getName() + "--->票卖出一张,还剩" + Tickets.tickets + "张");num--;}try {//卖了多张票之后放弃锁,通知生产者生产票Tickets.class.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}

3、主程序代码:

/*** 票卖方 .* * @author 小柱**/
public class Tickets {/*** 最大票数 .*/public final static int MAXNUMTICHETS;/*** 现有票数 .*/public static int tickets;static {MAXNUMTICHETS = 20;tickets = 0;}public static void main(String[] args) {// 票卖方Tickets tickets = new Tickets();// 生产者Provider provider = new Provider(tickets);// 消费者Consumer consumer = new Consumer(tickets);// 线程池启动生产者和消费者线程ExecutorService eService = Executors.newFixedThreadPool(10);eService.execute(provider);eService.execute(consumer);}/*** 卖票 .*/public void saleTickets() {//对应上面消费票的方法}/*** 生成票 .*/public void provideTickets() {//对应上面生产票的方法}
}//-------------------------------------------------/***票卖方线程   调用 消费票的动作.* * @author 小柱**/
class Consumer implements Runnable {/*** 票卖方 .*/private Tickets tickes;public Consumer(final Tickets tickets) {this.tickes = tickets;}@Overridepublic void run() {this.tickes.saleTickets();}}/*** 票生产方线程  调用生产票动作 .* * @author 小柱**/
class Provider implements Runnable {/*** 票卖方 .*/private Tickets tickes;public Provider(final Tickets tickets) {this.tickes = tickets;}@Overridepublic void run() {this.tickes.provideTickets();}}

部分运行结果:

pool-1-thread-1--->生产一张票,还有1张票
pool-1-thread-1--->生产一张票,还有2张票
pool-1-thread-1--->生产一张票,还有3张票
pool-1-thread-1--->生产一张票,还有4张票
pool-1-thread-1--->生产一张票,还有5张票
pool-1-thread-1--->生产一张票,还有6张票
pool-1-thread-1--->生产一张票,还有7张票
pool-1-thread-1--->生产一张票,还有8张票
pool-1-thread-1--->生产一张票,还有9张票
pool-1-thread-1--->生产一张票,还有10张票
pool-1-thread-1--->生产一张票,还有11张票
pool-1-thread-1--->生产一张票,还有12张票
pool-1-thread-1--->生产一张票,还有13张票
pool-1-thread-1--->生产一张票,还有14张票
pool-1-thread-1--->生产一张票,还有15张票
pool-1-thread-1--->生产一张票,还有16张票
pool-1-thread-1--->生产一张票,还有17张票
pool-1-thread-1--->生产一张票,还有18张票
pool-1-thread-2--->票卖出一张,还剩17张
pool-1-thread-2--->票卖出一张,还剩16张
pool-1-thread-2--->票卖出一张,还剩15张
pool-1-thread-1--->生产一张票,还有16张票
pool-1-thread-2--->票卖出一张,还剩15张
pool-1-thread-2--->票卖出一张,还剩14张
pool-1-thread-2--->票卖出一张,还剩13张
pool-1-thread-2--->票卖出一张,还剩12张
pool-1-thread-2--->票卖出一张,还剩11张
pool-1-thread-2--->票卖出一张,还剩10张
pool-1-thread-2--->票卖出一张,还剩9张
pool-1-thread-2--->票卖出一张,还剩8张
pool-1-thread-2--->票卖出一张,还剩7张
pool-1-thread-1--->生产一张票,还有8张票
pool-1-thread-1--->生产一张票,还有9张票
pool-1-thread-1--->生产一张票,还有10张票
pool-1-thread-1--->生产一张票,还有11张票
pool-1-thread-2--->票卖出一张,还剩10张
pool-1-thread-2--->票卖出一张,还剩9张
pool-1-thread-2--->票卖出一张,还剩8张
pool-1-thread-2--->票卖出一张,还剩7张
pool-1-thread-2--->票卖出一张,还剩6张
四、注意点

调用notify()和wait()方法必须在synchronized块里面调用;每次调用wait()方法等待的线程被唤醒之后,是接着wait()方法后面的代码继续执行,并非重新进入同步代码块;而且调用的这两个方法的对象一定是当前synchronized同步的锁对象,不然程序会报错,如下:

Exception in thread "pool-1-thread-1" Exception in thread "pool-1-thread-2" java.lang.IllegalMonitorStateExceptionat java.base/java.lang.Object.notify(Native Method)at com.concurent.test.Tickets.saleTickets(Tickets.java:55)at com.concurent.test.Consumer.run(Tickets.java:164)at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)at java.base/java.lang.Thread.run(Thread.java:844)

报错原因附上连接,我找到的解释报错原因解释的最好的一篇文章:

https://cloud.tencent.com/developer/article/1602598
总结

以上就是java中使用synchronized实现多线程同步安全的生产者消费者例子,注意的是,这种同步锁只能用在一个运行在一个虚拟机里面的多线程,如果是不同的虚拟机运行的多线程的同步安全需要用到分布式锁,这个稍微复杂一些。

java中synchronized同步锁实现生产者消费者模式相关推荐

  1. Java 中线程同步锁和互斥锁

    一 概述 1.1 互斥 所谓互斥,就是不同线程,通过竞争进入临界区(共享的数据和硬件资源),为了防止访问冲突,在有限的时间内只允许其中之一独占性的使用共享资源.如不允许同时写. 1.2 同步 同步关系 ...

  2. Java(二十二) -- 生产者消费者模式

    目录 生产者消费者模式 汉堡类 容器类 生产者 消费者 测试类 案例:多线程并发卖票 生产者消费者模式 在一个生产环境中,生产者和消费者在同一时间段内共享同一块缓冲区,生产者负责向缓冲区添加数据,消费 ...

  3. Java中synchronized同步块的执行流程

    必要知识 Java 对象的数据结构 在 HotSpot 虚拟机中,Java 对象在内存中存储的布局可以分为 3 块区域:对象头(Header).实例数据(Instance Data)和对齐填充(Pad ...

  4. Java中线程同步锁和互斥锁有啥区别?看完你还是一脸懵逼?

    首先不要钻概念牛角尖,这样没意义. 也许java语法层面包装成了sycnchronized或者明确的XXXLock,但是底层都是一样的.无非就是哪种写起来方便而已. 锁就是锁而已,避免多个线程对同一个 ...

  5. java 多线程 消费者_java中的多线程的实现生产者消费者模式

    丈夫类:往银行账户里存钱,存款[0~10000)的随机数,2秒存一次 妻子类:从银行账户里取钱,取款[0~10000)的随机数,2秒取一次,如果余额不足,等到丈夫存了钱,再取 public class ...

  6. 实现多线程、进程、线程、设置和获取线程名称、线程调度、线程控制、线程生命周期、同步代码块、线程安全的类、Lock锁、生产者消费者模式

    实现多线程:

  7. java 同步锁_java线程中的同步锁和互斥锁有什么区别?

    在java中,同步锁和互斥锁英文关键字都是Synchronized,没有本质上的区别,两者都包括对资源的独占,使用起来没有区别.概念上的区别是 1:互斥是通过竞争对资源的独占使用,彼此没有什么关系,执 ...

  8. java consumed_Java设计模式—生产者消费者模式(阻塞队列实现)

    生产者消费者模式是并发.多线程编程中经典的 真实世界中的生产者消费者模式 生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系.比如一个人正在准备食物(生产者),而另一个人正在吃(消费者) ...

  9. java中synchronized锁的升级(偏向锁、轻量级锁及重量级锁)

    java同步锁前置知识点 编码中如果使用锁可以使用synchronized关键字,对方法.代码块进行同步加锁 Synchronized同步锁是jvm内置的隐式锁(相对Lock,隐式加锁与释放) Syn ...

最新文章

  1. mat 内存分析 Linux,JVM内存分析工具MAT使用简介
  2. vue用公共组件页面传值_vuejs几种不同组件(页面)间传值的方式
  3. IEumberable和IQueryable的区别
  4. SQL_delete删除数据
  5. 小白程序员仅用 5 分钟入职 BAT,他只做了这件事!
  6. gun linux定义,linux gun make 入门
  7. 20190216-Java 教程(菜鸟教程)
  8. 亚瑟王的「随机」挑战:从交互到非交互式零知识证明——探索零知识证明系列(四)
  9. Windows远程桌面 无法进行复制粘贴的问题解决方法
  10. 人称代词I/my/mine/me 用法
  11. 最赏识王小川的,还是马化腾
  12. 外挂基础知识入门教学
  13. Hive安装过程中出现 The reference to entity createDatabaseIfNotExist must end with the ';' delimiter.问题
  14. BD第1课:天猫商城胸罩销售数据分析
  15. Why T - 为什么玩微博?
  16. android 设置铃声文件夹在哪里,Android 设置铃声的文件夹
  17. 历史上神秘消失的10天 | 历史全知道
  18. Dijkstra算法 python《地理信息系统导论》
  19. 被通知一个月后离职,我改了重要项目里的代码注释
  20. 出海先锋,平均15-16薪,上市公司汇量科技不容错过!

热门文章

  1. 学习黑客十余年,如何成为一名安全工程师?
  2. ANSYS APDL谐响应分析——悬臂梁的频响函数计算以及幅值、角度(相位)、分贝计算
  3. 微应用是什么_SpringCloud微服务架构篇1:微服务架构开发
  4. vscode插件安装介绍
  5. 声明中AutoEventWireup、Codebehind、Inherits分别表示什么意思?
  6. 通过 Java + wordgo 生成试卷
  7. cubemx 读卡器_STM32CubeMX基于SD卡的FATFS文件系统测试
  8. 前端百题斩【023】——赋值、浅拷贝、深拷贝大PK
  9. tplink迷你路由器中继模式_TP-link mini(迷你)无线路由器设置(Repeater模式)
  10. 雷锋网专访BiBoBox工作室:首个在WWDC上获奖的中国团队