生产者和消费者问题

synchronized版-> wait/notify

juc版->Lock

面试:单例模式、排序算法、生产者和消费者、死锁

生产者和消费者问题 Synchronized版

package org.example.pc;

public class A {
public static void main(String[] args) {
Date date = new Date();
new Thread(()->{
for (int i = 0; i < 20; i++) {
date.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
date.decrement();
}
},"B").start();
}
}
//判断等待、业务、通知
class Date{
private int number = 0; public synchronized void increment(){
if (number!=0){
try {
//不等于0就让该线程等待
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} number++;
// 打印加完后的值
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我完成了
this.notify();
}
public synchronized void decrement(){
if (number!=1){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notify();
} }

存在的问题:A、B、C、D四个线程

在线程中判断业务完成唤醒等待应该使用while循环判断,而非if判断,因为if判断值判断一次,在线程中存在一种状态叫虚假唤醒。

JUC版生产者和消费者问题

代码实现

package org.example.pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; //判断等待、业务、通知
public class Date {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition inCondition = lock.newCondition(); public void increment() { try {
lock.lock();
while (number != 0) {
inCondition.await();
}
number++;
// 打印加完后的值
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我完成了 inCondition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
} } public void decrement() { try {
lock.lock();
while (number != 1) {
inCondition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
inCondition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
} } }

Condition 精准的通知唤醒线程

在传统并发编程中,通过notifily唤醒线程后所有线程都是随机获取到资源的,JUC中可以通过Condition来精准的控制要唤醒哪一个线程资源。任何一个新技术的出现都不会只是为了实现之前已有的效果

代码实现

package org.example.pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class C {
public static void main(String[] args) {
DateC dateC = new DateC();
new Thread(()->{
for (int i = 0; i < 10; i++) dateC.plantA();
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) dateC.plantB();
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) dateC.plantC();
},"C").start();
} } class DateC {
private int number = 1;
private Lock lock = new ReentrantLock();
private Condition inCondition1 = lock.newCondition();
private Condition inCondition2 = lock.newCondition();
private Condition inCondition3 = lock.newCondition(); public void plantA(){
try {
lock.lock();
while (number!=1){
inCondition1.await();
}
System.out.println(Thread.currentThread().getName());
number=2;
inCondition2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void plantB(){
try {
lock.lock();
while (number!=2){
inCondition2.await();
}
System.out.println(Thread.currentThread().getName());
number=3;
inCondition3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void plantC(){
try {
lock.lock();
while (number!=3){
inCondition3.await();
}
System.out.println(Thread.currentThread().getName());
number=1;
inCondition1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}

JUC并发编程学习笔记(三)生产者和消费者问题的更多相关文章

  1. JUC并发编程学习笔记

    JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...

  2. Java并发编程学习笔记

    Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...

  3. 并发编程学习笔记(15)----Executor框架的使用

    Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...

  4. 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理

    1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...

  5. 并发编程学习笔记(13)----ConcurrentLinkedQueue(非阻塞队列)和BlockingQueue(阻塞队列)原理

    · 在并发编程中,我们有时候会需要使用到线程安全的队列,而在Java中如果我们需要实现队列可以有两种方式,一种是阻塞式队列.另一种是非阻塞式的队列,阻塞式队列采用锁来实现,而非阻塞式队列则是采用cas ...

  6. 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理

    在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...

  7. 并发编程学习笔记(8)----ThreadLocal的使用及源码分析

    1. ThreadLocal的理解 ThreadLocal,顾名思义,就是线程的本地变量,ThreadLocal会为每个线程创建一个本地变量副本,使得使用ThreadLocal管理的变量在多线程的环境 ...

  8. 并发编程学习笔记(6)----公平锁和ReentrantReadWriteLock使用及原理

    (一)公平锁 1.什么是公平锁? 公平锁指的是在某个线程释放锁之后,等待的线程获取锁的策略是以请求获取锁的时间为标准的,即使先请求获取锁的线程先拿到锁. 2.在java中的实现? 在java的并发包中 ...

  9. 并发编程学习笔记(5)----AbstractQueuedSynchronizer(AQS)原理及使用

    (一)什么是AQS? 阅读java文档可以知道,AbstractQueuedSynchronizer是实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量.事件,等等)提供一个框架, ...

  10. 并发编程学习笔记(4)----jdk5中提供的原子类及Lock使用及原理

    (1)jdk中原子类的使用: jdk5中提供了很多原子类,它会使变量的操作变成原子性的. 原子性:原子性指的是一个操作是不可中断的,即使是在多个线程一起操作的情况下,一个操作一旦开始,就不会被其他线程 ...

随机推荐

  1. 2021-11-29 Wpf的ViewModel和xaml绑定

    ViewModel代码 public class MainViewModel { MainWindow _mainWindow; public MainViewModel(MainWindow mai ...

  2. redis 中的 字符串

    String是redis 中的最基本的类型, 为二进制安全  ,意味着String可以表示各种类型  一个字符串value 最大为 521M set k1 v100 set k2 v200 get 命 ...

  3. Kafka主从模式和故障切换

    Kafka集群有主从模式吗? Kafka集群实际上并没有严格意义上的主从模式.Kafka的设计是基于分布式的,每个Topic都会切分为多个Partition,每个Partition都有一个Leader ...

  4. 跟进 .NET 8 Blazor 之 ReuseTabs 支持 Query 属性绑定

    ASP.NET 团队和社区在 .NET 8 继续全力投入 Blazor,为它带来了非常多的新特性,特别是在服务端渲染(SSR)方面,一定程度解决之前 WASM 加载慢,Server 性能不理想等局限性 ...

  5. 零基础入门——从零开始学习PHP反序列化笔记(二)

    魔术方法 魔术方法介绍 __construct() 触发时机:实例化对象之前 构造函数,在实例化一个对象的时候,首先会去自动执行的一个方法; <?php class User { public ...

  6. asp.net core之EfCore

    EF Core(Entity Framework Core)是一个轻量级.跨平台的对象关系映射(ORM)框架,用于在.NET应用程序中访问和操作数据库.它是Entity Framework的下一代版本 ...

  7. [go笔记]websocket入门

    简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议.WebSocket让客户端和服务端之间的数据交换变得非常简单,且允许服务器主动向客户端推送数据,并且之后客户端和服务端所有的通信都 ...

  8. Postgresql 批量插入命令COPY使用

    在很多场景下,我们经常会遇到将某个Excel或Csv文件中的数据,插入到Postgresql.对于这个需求,我们常规的处理办法就是将文件中的数据,按照文件表头名称转换成集合对象然后插入到数据库,当然这 ...

  9. 图解 LeetCode 算法汇总——回溯

    本文首发公众号:小码A梦 回溯算法是一种常见的算法,常见用于解决排列组合.排列问题.搜索问题等算法,在一个搜索空间中寻找所有的可能的解.通过向分支不断尝试获取所有的解,然后找到合适的解,找完一个分支后 ...

  10. 关闭k8s的pod时减小对服务的影响

    在应用程序的整个生命周期中,正在运行的 pod 会由于多种原因而终止.在某些情况下,Kubernetes 会因用户输入(例如更新或删除 Deployment 时)而终止 pod.在其他情况下,Kube ...