JUC并发编程学习笔记(三)生产者和消费者问题
生产者和消费者问题
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并发编程学习笔记(三)生产者和消费者问题的更多相关文章
- JUC并发编程学习笔记
JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...
- Java并发编程学习笔记
Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...
- 并发编程学习笔记(15)----Executor框架的使用
Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...
- 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理
1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...
- 并发编程学习笔记(13)----ConcurrentLinkedQueue(非阻塞队列)和BlockingQueue(阻塞队列)原理
· 在并发编程中,我们有时候会需要使用到线程安全的队列,而在Java中如果我们需要实现队列可以有两种方式,一种是阻塞式队列.另一种是非阻塞式的队列,阻塞式队列采用锁来实现,而非阻塞式队列则是采用cas ...
- 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理
在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...
- 并发编程学习笔记(8)----ThreadLocal的使用及源码分析
1. ThreadLocal的理解 ThreadLocal,顾名思义,就是线程的本地变量,ThreadLocal会为每个线程创建一个本地变量副本,使得使用ThreadLocal管理的变量在多线程的环境 ...
- 并发编程学习笔记(6)----公平锁和ReentrantReadWriteLock使用及原理
(一)公平锁 1.什么是公平锁? 公平锁指的是在某个线程释放锁之后,等待的线程获取锁的策略是以请求获取锁的时间为标准的,即使先请求获取锁的线程先拿到锁. 2.在java中的实现? 在java的并发包中 ...
- 并发编程学习笔记(5)----AbstractQueuedSynchronizer(AQS)原理及使用
(一)什么是AQS? 阅读java文档可以知道,AbstractQueuedSynchronizer是实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量.事件,等等)提供一个框架, ...
- 并发编程学习笔记(4)----jdk5中提供的原子类及Lock使用及原理
(1)jdk中原子类的使用: jdk5中提供了很多原子类,它会使变量的操作变成原子性的. 原子性:原子性指的是一个操作是不可中断的,即使是在多个线程一起操作的情况下,一个操作一旦开始,就不会被其他线程 ...
随机推荐
- linux内核vmlinux的编译过程之 --- vmlinux.o详解(八)
内核构建系统之所以要在链接 vmlinux 之前,去链接出vmlinux.o.其原因并不是要将 vmlinux.o 链接进 vmlinux,而是要在链接 vmlinux.o 的过程中做完两个动作: e ...
- 提示工程101|与 AI 交谈的技巧和艺术
随着 ChatGPT 的问世,人工智能(AI)新时代也正式开启.ChatGPT 是一种语言模型.它与用户进行对话交互,以便用户输入问题或提示,模型响应,然后对话可以继续来回进行,类似于在消息传递应用程 ...
- linux 问题: ssh登录报错,ssh_exchange_identification,多次几次可以登录
分析 怀疑是句柄数不够,和ssh的最大登录限制 确认 2.1 确认句柄数 过程: ~# systemctl status sshd | grep -i pid Main PID: 3767395 (s ...
- nginx搭建静态文件下载服务器
配置文件大致内容 server { # 监听8001端口 listen 8001; server_name 192.168.0.2; # 指定使用utf8的编码 charset utf-8; # 内容 ...
- 3、Spring之入门案例
3.1.创建module 3.1.1.右击project,创建新module 3.1.2.选择maven 3.1.3.设置module名称和路径 3.1.4.module初始状态 3.1.5.配置打包 ...
- 优化Redis缓存淘汰机制解决性能测试中报错率逐渐攀升问题
在某个查询场景的性能测试过程中,遇到了一个问题:测试过程中报错率逐渐攀升.进一步检查后发现,在查询业务所在应用的后台日志和平台应用的后台日志中,都出现了用户登录相关的报错信息.经过排查分析,发现了问题 ...
- Azure Storage 系列(八)存储类型细化分类说明
一,引言 Azure 存储账户功能经过官方改进迭代后,在创建的时候,存储账户的类型被分为两大类: 1)general-purpose v2 account(标准常规用途v2) Blob 存储,队列存储 ...
- MindSponge分子动力学模拟——定义一个分子系统(2023.08)
技术背景 在前面两篇文章中,我们分别介绍了分子动力学模拟软件MindSponge的软件架构和安装与使用教程.这里我们进入到实用化阶段,假定大家都已经在本地部署好了基于MindSpore的MindSpo ...
- 如何使用关键词搜索API接口获取到快手的商品
如果您想使用关键词搜索API接口获取到快手的商品,可以通过以下步骤实现: 1. 首先注册账号.根据文档申请相应的接口权限. 2. 确定需要使用的API接口.对于商品搜索,您可以查看相关的API文档以获 ...
- 每日一库:pprof简介
pprof简介 pprof是Go语言的一个性能分析库,它可以帮助开发者找出程序中的性能瓶颈.pprof提供了CPU分析.内存分析.阻塞分析等多种性能分析功能. 以下是pprof的主要特性: CPU分析 ...