问题描述

题目:两个线程操作一个变量,实现两个线程对同一个资源一个进行加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. windows系统下安装最新版gym的安装方法(此时最新版的gym为0.24.0,gym==0.24.0)

    当前gym的最新版本为0.24.0,本篇介绍对gym[atari]==0.24.0进行安装. 使用pip安装: pip install gym[atari] 可以看到此时安装的是ale_py而不是at ...

  2. flex 左右布局-----在手机端,当左侧宽度固定,右侧宽度自适应时,右侧会超出浏览器窗口的问题解决方案

    废话不多说:直接上代码解决问题: 父级: .parent { display: flex; flex-flow: row; } 子级: .left-child { width:100px; } .ri ...

  3. JS Map对象与map方法

    前言  最近遇到一个遍历的问题,查资料的过程中,发现有个 Map 对象,提供了很多方法可以轻松地获取我们想要的数据,之前只知道有 map ,没想到还有 Map ,是不是有点绕?不急,这两个东西都是虽然 ...

  4. ChatGLM

    ChatGLM: A Family of Large Language Models from GLM-130B to GLM-4 All Tools(2024.7.16) Code:https:// ...

  5. Go 链路追踪入门 Opentelemetry

    前言 Opentelemetry 分布式链路跟踪( Distributed Tracing )的概念最早是由 Google 提出来的,发展至今技术已经比较成熟,也是有一些协议标准可以参考.目前在 Tr ...

  6. tlmgr 操作

    宏包管理 sudo tlmgr install <package> # 安装宏包 sudo tlmgr install scheme-full # 安装全部宏包 sudo tlmgr re ...

  7. 【测试平台开发】——01后端web开发框架Flask

    官方中文地址:https://flask.net.cn/ 官方英文地址:https://flask.palletsprojects.com/en/2.1.x/ github地址:https://git ...

  8. layui的各模块的基本用途是什么?

    Layui 是一个流行的前端UI框架,提供了多个模块,每个模块有不同的功能和用途.以下是 Layui 的一些主要模块以及它们的基本用途: 核心模块(layui): Layui 的核心模块包含了一些核心 ...

  9. get方法传参后端接收数据异常 - 特殊字符需转义

    get方法传参的时候,如果有特殊字符,如 + 等,无法被识别,导致后端处理异常,所以,get方式,如果有特殊字符,需要转义后再请求接口 1.java 特殊字符转义 URLEncoder.encode( ...

  10. 利用分布式锁在ASP.NET Core中实现防抖

    前言 在 Web 应用开发过程中,防抖(Debounce) 是确保同一操作在短时间内不会被重复触发的一种有效手段.常见的场景包括防止用户在短时间内重复提交表单,或者避免多次点击按钮导致后台服务执行多次 ...