问题描述

题目:两个线程操作一个变量,实现两个线程对同一个资源一个进行加1操作,另外一个进行减1操作,且需要交替实现,变量的初始值为0。即两个线程对同一个资源进行加一减一交替操作。话不多说,开干
首先我们先定义操作的资源,并且定义方法。

首先定义资源类

//资源类
class Resource {
private int number = 0; public synchronized void up() throws InterruptedException {
//1.判断
if(number != 0) {
this.wait();
}
//2.干活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
this.notifyAll();
} public synchronized void down() throws InterruptedException {
if(number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
this.notifyAll();
}
}

接着我们写我们的两个线程

public class ThreadWaitNotifyDemo {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
resource.up();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
resource.down();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
}

结果如下图

这里我们通过Lambda表达式来通过匿名内部类来创建线程并启动,可以看到一个线程up方法,另外一个线程进行down方法,
首先比如线程A先判断是否为0,不为0,那么此时进行加1操作,同一时刻的B线程此时判断的是number等于0,那么就进行等待操作,这个时候A线程加完了,然后通过notifyAll()方法来唤醒其他的线程,所以就完成了减的操作,这里for0~10是为了保证能够交替进行10次。

wait 方法和notify方法是Thread的方法吗?

这里扩展一个知识点,wait 方法和notify方法是Thread的方法吗?
答案:错错错 。
查看API我们可以见到这两个方法属于Object的方法,wait 和 notify 必须要配合synchronized 关键字使用。

这就完了吗??

需求变更

不不不,此时需求改动了!这个时候项目经理过来说,小王,我不要两个线程操作了,我要四个线程同时操作,两个进行相加,两个进行相减,还是交替到时必须是0,1相互的交替。
这个时候心想,简单呀,我再多加两个线程!
于是,多加了两个线程。
代码如下,Resource不变。

public class ThreadWaitNotifyDemo {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
resource.up();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
resource.down();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
resource.up();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
resource.down();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}

和之前的一样,多加了两个线程,一个进行加,一个进行减操作。
然后run一下,结果如下图:

这里我们就看到,有的为3了,这是什么情况???

分析:举个例子,A,C线程是进行加操作,B,D是进行减操作,此时如果是0,那么B,和D线程是被wait了的对吧,A和C因为up方法被加了锁,所以只有一个方法进行加,如果此时A进行完操作,然后再notifyall,那么此时C线程也会进行up操作,

因为C线程在if(number!=0){this.wait();}这里被唤醒后,继续进行其他的操作了,且在A执行完加1操作和后没有继续判断number的情况,同理B,D线程也是如此,所以就会出现上图的情况!
那么这种情况怎么解决呢??

解决方案

因为我们是没有重新进行判断,那么,我们让其重新进行判断就是了!
修改资源类代码如下:

class Resource {
private int number = 0; public synchronized void up() throws InterruptedException {
//1.判断
while (number != 0) {
this.wait();
}
//2.干活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
this.notifyAll();
} public synchronized void down() throws InterruptedException {
while (number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
this.notifyAll();
}
}

四个线程的操作资源的代码不变,结果如下:

Java高并发synchronized讲解生产者消费者的更多相关文章

  1. 如何解决java高并发详细讲解

    对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研 ...

  2. java 多线程并发系列之 生产者消费者模式的两种实现

    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据 ...

  3. 多道技术 进程 线程 协程 GIL锁 同步异步 高并发的解决方案 生产者消费者模型

    本文基本内容 多道技术 进程 线程 协程 并发 多线程 多进程 线程池 进程池 GIL锁 互斥锁 网络IO 同步 异步等 实现高并发的几种方式 协程:单线程实现并发 一 多道技术 产生背景 所有程序串 ...

  4. java高并发系列 - 第10天:线程安全和synchronized关键字

    这是并发系列第10篇文章. 什么是线程安全? 当多个线程去访问同一个类(对象或方法)的时候,该类都能表现出正常的行为(与自己预想的结果一致),那我们就可以所这个类是线程安全的. 看一段代码: pack ...

  5. java高并发编程(三)

    java高并发主要有三块知识点: synchronizer:同步器,在多个线程之间互相之间怎么进行通讯,同步等: 同步容器:jdk提供了同步性的容器,比如concurrentMap,concurren ...

  6. 《实战Java高并发程序设计》读书笔记

    文章目录 第二章 Java并行程序基础 2.1 线程的基本操作 2.1.1 线程中断 2.1.2 等待(wait)和通知(notify) 2.1.3 等待线程结束(join)和谦让(yield) 2. ...

  7. java高并发系列 - 第25天:掌握JUC中的阻塞队列

    这是java高并发系列第25篇文章. 环境:jdk1.8. 本文内容 掌握Queue.BlockingQueue接口中常用的方法 介绍6中阻塞队列,及相关场景示例 重点掌握4种常用的阻塞队列 Queu ...

  8. java高并发系列 - 第13天:JUC中的Condition对象

    本文目标: synchronized中实现线程等待和唤醒 Condition简介及常用方法介绍及相关示例 使用Condition实现生产者消费者 使用Condition实现同步阻塞队列 Object对 ...

  9. Java高并发系列——检视阅读

    Java高并发系列--检视阅读 参考 java高并发系列 liaoxuefeng Java教程 CompletableFuture AQS原理没讲,需要找资料补充. JUC中常见的集合原来没讲,比如C ...

  10. 关于Java高并发编程你需要知道的“升段攻略”

    关于Java高并发编程你需要知道的"升段攻略" 基础 Thread对象调用start()方法包含的步骤 通过jvm告诉操作系统创建Thread 操作系统开辟内存并使用Windows ...

随机推荐

  1. 使用 defineNuxtRouteMiddleware 创建路由中间件

    title: 使用 defineNuxtRouteMiddleware 创建路由中间件 date: 2024/8/10 updated: 2024/8/10 author: cmdragon exce ...

  2. 前端黑科技:使用 JavaScript 实现网页扫码功能

    在数字化时代,二维码已经渗透到我们生活的方方面面.从移动支付到产品溯源,二维码凭借其便捷性和高效性,成为了信息传递的重要载体.而随着前端技术的不断发展,我们甚至可以使用 JavaScript 在网页端 ...

  3. vmware创建虚拟机

    1.vmware创建麒麟虚拟机 选择安装程序光盘映像文件,会最小化安装桌面版本,新的镜像可能识别不到,比如麒麟等. 麒麟系统类似于欧拉,欧拉类似于CentOS,所以我们选择CentOS 修改虚拟机名称 ...

  4. Ruby 学习笔记

    基本语法 变量 name = "Alice" age = 30 puts "Name: #{name}, Age: #{age}" var # 局部变量 @va ...

  5. 使用 Portainer CE 管理 Docker

    此文档参考官方文档 Install Portainer CE with Docker on Linux 编写. 创建容器 docker volume create portainer_data 启动 ...

  6. git 修改提交作者及提交日期

    进入交互式 rebase 模式 git rebase -i <commit> 你要修改哪次提交的日期,就 rebase 到该提交的上一次提交. git 提示你新的分支要包含哪些提交,默认已 ...

  7. Prometheus 告警恢复时,怎么获取恢复时的值?

    Prometheus 告警事件中的 $value 表示当前告警触发时的值,但是在告警恢复时,Resolved 事件中的 $value 仍然是最新告警时的值,并非是恢复时的值,这是什么原因和原理?是否有 ...

  8. YuebonCore:基于.NET8开源、免费的权限管理及快速开发框架

    前言 今天大姚给大家分享一款基于.NET8开源.免费(MIT License)功能强大的权限管理及快速开发框架,支持前后端分离,项目架构易于扩展,是中小企业的首选:YuebonNetCore. 核心设 ...

  9. 【YashanDB知识库】汇聚库23.1环境发生coredump

    [标题]汇聚库23.1环境发生coredump [问题分类]数据库错误 [关键词]YashanDB, 汇聚库, coredump [问题描述]在23.1.1.200版本数据库环境创建dblink.视图 ...

  10. Coursera self-driving2, State Estimation and Localization Week2, kalman filter 卡尔曼滤波

    KF - Kalman Filter: EKF - Extended Kalman Filter: ES-EKF - Error State Extended Kalman Filter 和EKF一样 ...