首页 > 编程语言 >JUC并发编程学习笔记(三)生产者和消费者问题

JUC并发编程学习笔记(三)生产者和消费者问题

时间:2023-11-02 20:11:05浏览次数:32  
标签:JUC Thread lock void 编程 number 并发 new public

生产者和消费者问题

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,Thread,lock,void,编程,number,并发,new,public
From: https://www.cnblogs.com/nhgtx/p/17805964.html

相关文章

  • JUC并发编程学习笔记(二)Lock锁(重点)
    Lock锁(重点)传统的synchronized传统的解决多线程并发导致的一些问题我们会使用synchronized关键字来解决,synchronized的本质就是队列、锁。Lock的实现类有:可重复锁(最常用)、读锁、写锁在创建可重复锁时,可传入boolean类型值来决定该锁是公平锁(先来后到)还是非公平锁(可插队)......
  • Java-并发编程-进阶篇
    在上一篇幅中对并发编程进行了简单介绍:并发与并行,进程与线程,以及并发编程的简单代码但是在企业中往往并不能解决实际问题,例如:1.synchronized关键字在企业开发中会大大降低系统的性能2.当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象......
  • JUC并发编程学习笔记(一)认知进程和线程
    进程和线程进程一个程序,如QQ.exe,是程序的集合一个进程往往可以包含多个线程,至少包含一个java默认有两个线程,GC垃圾回收线程和Main线程线程:一个进程中的各个功能java无法真正的开启线程,因为java是运行在虚拟机上的,所以只能通过C++,通过native本地方法调用C++开启线程priva......
  • (四)C#编程基础复习——运算符
    运算符其实就是一个符号,用来告诉编译器执行特定的数学或者逻辑运算。C#中内置了丰富的运算符,大致可以分为以下几类:一、算术运算符算术运算符即完成特定算术运算的符号,例如加、减、乘、除、余等,如下图所示:inta=10;intb=20;Console.WriteLine("a+b={0}",a+b);Console.W......
  • 【转载】CUDA编程学习记录 C++
    参考Yuezero的CUDA编程基础(https://blog.csdn.net/weixin_54338498/article/details/127947551)CUDA编程模型host指代CPU及其内存,包含host程序device指代GPU及其内存,包含device程序经典CUDA程序的执行流程如下:分配host内存,并进行数据初始化;分配device内存,并从host将......
  • (三)C#编程基础复习——数据类型
    C#语言中内置了一些基本的数据类型,数据类型用来指定程序中变量可以存储的数据的类型,C#中的数据类型可以大致分为三类:值类型(Valuetypes);引类型(Referencestypes);指针类型(Pointertypes);一、值类型值类型顾名思义就是有带数值的类型,C#中的值类型有非常多,值类型变量声明后,不......
  • 模拟实现.net中的Task机制:探索异步编程的奥秘
    .net中使用Task可以方便地编写异步程序,为了更好地理解Task及其调度机制,接下来模拟Task的实现,目的是搞清楚:Task是什么Task是如何被调度的基本的Task模拟实现从最基本的Task用法开始Task.Run(Actionaction)这个命令的作用是将action作为一项任务提交给调度器,调度器会安排......
  • 系统编程:控制文件I/O的内核缓冲之sync(),fsync()和fdatasync()
        通过系统编程:从write()和fwrite()谈开来我们知道了系统调用和glibc库函数为了提升性能而设立的缓冲区,那么,什么情况下数据会从上一次缓冲区刷新到下一层存储介质(可能是缓冲区,也可能是永久存储介质)呢?fflush()库函数提供了强制将stdio库函数缓冲区数据刷新到内核缓冲......
  • 实验3 C语言函数应用编程
    任务1源码1#include<stdio.h>2#include<stdlib.h>3#include<time.h>4#include<windows.h>5#defineN806voidprint_text(intline,intcol,chartext[]);//函数声明7voidprint_spaces(intn);//函数声明8voidprint_bl......
  • 飞腾派使用内核态编程完成LED20控制操作
    1基础知识在该程序设计过程中我们首先需要学习如何在内核态编程。1.1内核态编程在内核态中编写C语言程序和在用户态中编写C语言程序不同,在用户态中编写C语言程序,我们可以使用libc库,通过系统调用访问内核态的相关操作。基础的内核态程序如下:#include<linux/init.h>#include......