Java基础 浅谈线程补充 -------lock实现消费者和生产者以及精确唤醒

本文主要是介绍Java基础 浅谈线程补充 -------lock实现消费者和生产者以及精确唤醒,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们一直使用synchronized来聊经典模式消费者与生产者,在同步安全的时候我们除了讲解synchronized以外,还讲解了lock,对数据进行安全进行保护。那么问题就来了,是否可以通过lock实现消费者和生产者呢?

答案肯定是能,但是应该好奇是如何实现?

先看代码


class Data{private int num=0;Lock lock= new ReentrantLock();public void increment(){lock.lock();try {while (num!=0) {lock.wait();} }catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {lock.unlock();} num++;System.out.println(Thread.currentThread().getName()+"现在是number="+num);lock.notifyAll();}public void decrement(){lock.lock();try {while (num==0) {lock.wait();} }catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {lock.unlock();} num--;System.out.println(Thread.currentThread().getName()+"现在是number="+num);lock.notinofyAll();}} public class Test {public static void main(String[] args) {Data  data=new Data();new Thread(()->{for (int i=0;i<10;i++){data.decrement();}},"线程A").start();new Thread(()->{for(int i=0;i<10;i++) {data.increment();}} , "线程b").start() ;}
}
//输出
Exception in thread "线程A" Exception in thread "线程b" 线程b现在是number=1java.lang.IllegalMonitorStateExceptionat java.lang.Object.wait(Native Method)at java.lang.Object.wait(Unknown Source)at test.Data.decrement(Test.java:66)at test.Test.lambda$0(Test.java:22)at java.lang.Thread.run(Unknown Source)
java.lang.IllegalMonitorStateExceptionat java.lang.Object.notifyAll(Native Method)at test.Data.increment(Test.java:58)at test.Test.lambda$1(Test.java:31)at java.lang.Thread.run(Unknown Source)

只是简单的讲synchronized换程了lock和unlock,代码编辑器提示可以使用wait方法呢notinofyAll方法,所以第一反应,这个简单,可以直接依葫芦画瓢,然而输出的确实打脸了。

这个异常时一个非法异常,说明在编译没有异常,而是一个运行异常。还有就是为什么可以编译使用lock调用wait?

这个需要看jdk帮助文档:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f8GZHwVG-1604646302129)(C:\Users\jhfan\AppData\Roaming\Typora\typora-user-images\image-20201105164725110.png)]

可以看出这继承了object,所以可以使用object中的方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FLQCmPiA-1604646302153)(C:\Users\jhfan\AppData\Roaming\Typora\typora-user-images\image-20201105165027174.png)]

我们在看 ReentrantLock详细放的时候发现了这个,所以我们可以再看condition是如何实现等待和唤醒功能的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xZ3XzM9S-1604646302157)(C:\Users\jhfan\AppData\Roaming\Typora\typora-user-images\image-20201105165426933.png)]

完美不但对功能进行了一个简单解析,而且还给予了一个例子模板,可以从中找出三个方法await(),signal().signalAll()。

其中await等待,可以设置时间,signal唤醒一个等待的线程,signalAll唤醒所有等待的线程。

现在我们就可以通过lock实现消费者和生产者

public class Test {public static void main(String[] args) {Data  data=new Data();new Thread(()->{for (int i=0;i<10;i++){data.decrement();}},"线程A").start();new Thread(()->{for(int i=0;i<10;i++) {data.increment();}} , "线程b").start() ;}
}class Data{private int num=0;Lock lock= new ReentrantLock();Condition notFull  = lock.newCondition(); Condition notEmpty = lock.newCondition(); public void increment(){lock.lock();try {while (num!=0) {notEmpty.await();} num++;System.out.println(Thread.currentThread().getName()+"现在是number="+num);notFull.signalAll();}catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {lock.unlock();} }public void decrement(){lock.lock();try {while (num==0) {notFull.await();} num--;System.out.println(Thread.currentThread().getName()+"现在是number="+num);notEmpty.signalAll();}catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {lock.unlock();} }
} 
//输出
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0
线程b现在是number=1
线程A现在是number=0

可见输出可以完成用synchronized实现的消费者和生产者的例子。但是通过上面我们可以察觉一件事,那就是conditon,可以实现精确唤醒,比如increment中的condition等待的是notEmpty,而唤醒的时候使用的是notFull。

所以可以实现精确唤醒,具体看代码。

public class Test {public static void main(String[] args) {Step  step=new Step();new Thread(()->{for (int i = 0; i <10; i++) {step.step_first();}},"线程A").start();new Thread(()->{for (int i = 0; i <10; i++) {step.step_second();}},"线程b").start();new Thread(()->{for (int i = 0; i <10; i++) {step.step_thrid();}},"线程c").start();}}class Step{int num=1;Lock lock= new ReentrantLock();Condition step1  = lock.newCondition();Condition step2  = lock.newCondition();Condition step3  = lock.newCondition();public void step_first() {lock.lock();try {while(num!=1) {step1.await();}System.out.println(Thread.currentThread().getName());num=2;step2.signalAll();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {lock.unlock();}}public void step_second() {lock.lock();try {while(num!=2) {step2.await();}System.out.println(Thread.currentThread().getName());num=3;step3.signalAll();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {lock.unlock();}}public void step_thrid() {lock.lock();try {while(num!=3) {step3.await();}System.out.println(Thread.currentThread().getName());num=1;step1.signalAll();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {lock.unlock();}}
}
//输出
线程A
线程b
线程c
线程A
线程b
线程c
线程A
线程b
线程c
线程A
线程b
线程c
线程A
线程b
线程c
线程A
线程b
线程c
//后面一样

可以看出同各国condition可以实现精准唤醒。a,b,c相互交替,这个可以实现比如购物支付这样的功能。

这篇关于Java基础 浅谈线程补充 -------lock实现消费者和生产者以及精确唤醒的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1073129

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听