问题描述

题目:两个线程操作一个变量,实现两个线程对同一个资源一个进行加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. Jupyter 实验室中的 GPU 仪表板

    这两天收到了NVIDIA公司推送的新闻: https://developer.nvidia.com/zh-cn/blog/gpu-dashboards-in-jupyter-lab/?ncid=em- ...

  2. 【转载】回复“大修意见”(Major Revision)的模板 —— 审稿意见回复模板

    原文地址: https://zhuanlan.zhihu.com/p/80214252 ================================================== 上周有个小 ...

  3. tensorflow_probability.python.bijectors的一些使用

    网上见到一个TensorFlow的代码,没见过这个形式的,是概率编程的代码: # coding=utf-8 # Copyright 2020 The TF-Agents Authors. # # Li ...

  4. Functional PHP (通义千问)

    Functional PHP 是一个专为 PHP 开发者设计的库,旨在引入函数式编程的概念和工具,帮助开发者编写更高效.可读性强的代码.以下是几个使用 Functional PHP 库进行函数式编程的 ...

  5. Linux的netns使用总结

    转载请注明出处: Linux的netns(Network Namespace)是Linux内核提供的一项强大的网络隔离功能,它能够创建多个独立的网络空间,每个空间都拥有自己独立的网络协议栈,包括网络接 ...

  6. 9组-Beta冲刺-总结

    一.基本情况 组长博客链接:9组-Beta冲刺-总结 现场答辩总结:本次答辩,我们演示了我们到Beta冲刺周结束时的成果展示,离目标还有一些距离,不过本次答辩完成了任务,总体来说还不错,希望下次最终答 ...

  7. 跨越时空的对话:如何使用AI阅读工具ChatDOC快速建立数字化身?

    跨越时空的对话:如何使用 ChatDOC 快速建立数字化身?以史蒂夫·乔布斯 AI 为例 开门见山,这篇文章主要介绍如何将 AI 改造为靠谱.好用.基于某个人物的数字化身.比如,乔布斯 AI.马斯克 ...

  8. 20-canvas之形变

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  9. Kummer 定理

    \(n!\) 中含素数 \(p\) 的幂次为 \(\displaystyle\sum_{i=1}\lfloor\frac{n}{p^{i}}\rfloor\) Kummer 定理:\({n+m\cho ...

  10. 【CMake系列】06-项目结构与输出路径管理

    为了对大型项目实现更好的管理[模块化协作开发等等],cmake 提供了很多指令,可以对项目的结构进行调整.管理,便于项目的合理规划.本文我们要学习的就是 项目结构的设置,以及 构建程序等 输出路径的设 ...