线程系列3--Java线程同步通信技术

       上一篇文章我们讲解了线程间的互斥技术,使用关键字synchronize来实现线程间的互斥技术。根据不同的业务情况,我们可以选择某一种互斥的方法来实现线程间的互斥调用。例如:自定义对象实现互斥(synchronize("自定义对象"){}),同一个类实例对象(synchronize(this){}),类的字节码对象(synchronize(字节码对象){})。这三种方法均可实现线程间的互斥,我们实际运用中灵活使用。

下面进入今天的正题:线程--线程间的同步通信技术;我们还是以传智播客中的代码为例来讲解,子线程运行10次,主线程运行10次,如此交替运行50次。

首先看不适用同步技术时的问题代码:

public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {for(int i=1;i<=50;i++){for(int j=1;j<=10;j++){System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}}}}).start();for(int i=1;i<=50;i++){for(int j=1;j<=10;j++){System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}}
}

上面代码的运行结果可知,子线程与主线程间是杂乱无章的运行,显然不能满足我的要求。那我们来稍作调整。代码如下:

public class ThreadSynchronizedTechnology {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {for(int i=1;i<=50;i++){
                       //使用类的字节码作为互斥对象synchronized(ThreadSynchronizedTechnology.class){for(int j=1;j<=10;j++){System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}}}}}).start();for(int i=1;i<=50;i++){synchronized(ThreadSynchronizedTechnology.class){for(int j=1;j<=10;j++){System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}}}}}

上面的代码我们使用类的字节码作为互斥对象,显然程序不再是杂乱无章的运行,子线程与主线程都能完整的运行完,但是没有实现我们要求的交替运行,不要急我接着调整,我非常喜欢张孝祥老师循序渐进的讲课方式(我不是托,是发自内心的),下面我们接着调整:

public class ThreadSynchronizedTechnology {public static void main(String[] args) {final Songzl song = new Songzl();new Thread(new Runnable() {@Overridepublic void run() {for(int i=1;i<=50;i++){song.sub(i);}}}).start();for(int i=1;i<=50;i++){song.main(i);}}
}
class Songzl{//子线程运行的方法public void sub(int i){synchronized(Songzl.class){for(int j=1;j<=10;j++){System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}}}//主线程运行的方法public void main(int i){synchronized(Songzl.class){for(int j=1;j<=10;j++){System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}}}
}

打印结果依然不是交替运行,我调成这样是为了体现编程的面向对象思想,将相关联的方法封装到同一个类中,方便代码运维。我们接着调整代码:

public class ThreadSynchronizedTechnology {public static void main(String[] args) {final Songzl song = new Songzl();new Thread(new Runnable() {@Overridepublic void run() {for(int i=1;i<=50;i++){song.sub(i);}}}).start();for(int i=1;i<=50;i++){song.main(i);}}
}
class Songzl{//实现线程同步通信,互斥的方法共享次变量private boolean jiaoti = true;//子线程运行的方法:同一个类中方法互斥,类似与synchronized(this){}public synchronized void sub(int i) {if(!jiaoti){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}for(int j=1;j<=10;j++){System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}jiaoti = false;this.notify();}//主线程运行的方法:同一个类中方法互斥,类似与synchronized(this){}public synchronized void main(int i){if(jiaoti){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}for(int j=1;j<=10;j++){System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}jiaoti = true;this.notify();}
}

打印结果可见,已经实现子线程和主线程有条不紊的交替运行,线程间既能互斥,同时又可以相互同步通信运行;线程的互斥是通过synchronized实现的,线程间的同步通信是两个线程间共同持有一个变量来实现的。但是线程有一个“假唤醒”的情况,虽然发生率低,但是我们不能忽略,继续调整代码:

public class ThreadSynchronizedTechnology {public static void main(String[] args) {final Songzl song = new Songzl();new Thread(new Runnable() {@Overridepublic void run() {for(int i=1;i<=50;i++){song.sub(i);}}}).start();for(int i=1;i<=50;i++){song.main(i);}}
}
class Songzl{//实现线程同步通信,互斥的方法共享次变量private boolean jiaoti = true;//子线程运行的方法:同一个类中方法互斥,类似与synchronized(this){}public synchronized void sub(int i) {//避免线程的假唤醒while(!jiaoti){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}for(int j=1;j<=10;j++){System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}jiaoti = false;this.notify();}//主线程运行的方法:同一个类中方法互斥,类似与synchronized(this){}public synchronized void main(int i){//避免线程的假唤醒while(jiaoti){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}for(int j=1;j<=10;j++){System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");}jiaoti = true;this.notify();}
}

我们使用while循环来避免这种假唤醒的情况,当CPU任性的给与不该执行的线程、或者线程神经病的自己唤醒自己,我们可以使用while循环来避免上述情况。好了到此为止,代码已经完全满足我们的需求了。通过上面代码的循序渐进,我们很容易理解线程间的同步与互斥技术。

总结:线程之间的制约关系?

       当线程并发执行时,由于资源共享和线程协作,使用线程之间会存在以下两种制约关系。

     (1)间接相互制约。一个系统中的多个线程必然要共享某种系统资源,如共享CPU,共享I/O设备,所谓间接相互制约即源于这种资源共享,打印机就是最好的例子,线程A在使用打印机时,其它线程都要等待。

     (2)直接相互制约。这种制约主要是因为线程之间的合作,如有线程A将计算结果提供给线程B作进一步处理,那么线程B在线程A将数据送达之前都将处于阻塞状态。

       间接相互制约可以称为互斥,直接相互制约可以称为同步,对于互斥可以这样理解,线程A和线程B互斥访问某个资源则它们之间就会产个顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步

 

转载于:https://www.cnblogs.com/aoshicangqiong/p/7707288.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/391044.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python数据结构之四——set(集合)

Python版本&#xff1a;3.6.2 操作系统&#xff1a;Windows 作者&#xff1a;SmallWZQ 经过几天的回顾和学习&#xff0c;我终于把Python 3.x中的基础知识介绍好啦。下面将要继续什么呢&#xff1f;让我想想先~~~嗯&#xff0c;还是先整理一下近期有关Python基础知识的随笔吧…

volatile关键字有什么用

问题&#xff1a;volatile关键字有什么用 在工作的时候&#xff0c;我碰到了volatile关键字。但是我不是非常了解它。我发现了这个解释 这篇文章已经解释了问题中的关键字的细节了&#xff0c;你们曾经用过它吗或者见过正确使用这个关键字的样例 回答 Java中同步的实现大多是…

knn 机器学习_机器学习:通过预测意大利葡萄酒的品种来观察KNN的工作方式

knn 机器学习Introduction介绍 For this article, I’d like to introduce you to KNN with a practical example.对于本文&#xff0c;我想通过一个实际的例子向您介绍KNN。 I will consider one of my project that you can find in my GitHub profile. For this project, …

MMU内存管理单元(看书笔记)

http://note.youdao.com/noteshare?id8e12abd45bba955f73874450e5d62b5b&subD09C7B51049D4F88959668B60B1263B5 笔记放在了有道云上面了&#xff0c;不想再写一遍了。 韦东山《嵌入式linux完全开发手册》看书笔记转载于:https://www.cnblogs.com/coversky/p/7709381.html

Java中如何读取文件夹下的所有文件

问题&#xff1a;Java中如何读取文件夹下的所有文件 Java里面是如何读取一个文件夹下的所有文件的&#xff1f; 回答一 public void listFilesForFolder(final File folder) {for (final File fileEntry : folder.listFiles()) {if (fileEntry.isDirectory()) {listFilesFor…

github pages_如何使用GitHub Actions和Pages发布GitHub事件数据

github pagesTeams who work on GitHub rely on event data to collaborate. The data recorded as issues, pull requests, and comments become vital to understanding the project.在GitHub上工作的团队依靠事件数据进行协作。 记录为问题&#xff0c;请求和注释的数据对于…

c# .Net 缓存 使用System.Runtime.Caching 做缓存 平滑过期,绝对过期

1 public class CacheHeloer2 {3 4 /// <summary>5 /// 默认缓存6 /// </summary>7 private static CacheHeloer Default { get { return new CacheHeloer(); } }8 9 /// <summary>10 /// 缓存初始化11 /// </summary>12 …

python 实现分步累加_Python网页爬取分步指南

python 实现分步累加As data scientists, we are always on the look for new data and information to analyze and manipulate. One of the main approaches to find data right now is scraping the web for a particular inquiry.作为数据科学家&#xff0c;我们一直在寻找…

Java 到底有没有析构函数呢?

Java 到底有没有析构函数呢&#xff1f; ​ ​ Java 到底有没有析构函数呢&#xff1f;我没能找到任何有关找个的文档。如果没有的话&#xff0c;我要怎么样才能达到一样的效果&#xff1f; ​ ​ ​ 为了使得我的问题更加具体&#xff0c;我写了一个应用程序去处理数据并且说…

关于双黑洞和引力波,LIGO科学家回答了这7个你可能会关心的问题

引力波的成功探测&#xff0c;就像双黑洞的碰撞一样&#xff0c;一石激起千层浪。 关于双黑洞和引力波&#xff0c;LIGO科学家回答了这7个你可能会关心的问题 最近&#xff0c;引力波的成功探测&#xff0c;就像双黑洞的碰撞一样&#xff0c;一石激起千层浪。 大家兴奋之余&am…

如何使用HTML,CSS和JavaScript构建技巧计算器

A Tip Calculator is a calculator that calculates a tip based on the percentage of the total bill.小费计算器是根据总账单的百分比计算小费的计算器。 Lets build one now.让我们现在建立一个。 第1步-HTML&#xff1a; (Step 1 - HTML:) We create a form in order to…

用于MLOps的MLflow简介第1部分:Anaconda环境

在这三部分的博客中跟随了演示之后&#xff0c;您将能够&#xff1a; (After following along with the demos in this three part blog you will be able to:) Understand how you and your Data Science teams can improve your MLOps practices using MLflow 了解您和您的数…

[WCF] - 使用 [DataMember] 标记的数据契约需要声明 Set 方法

WCF 数据结构中返回的只读属性 TotalCount 也需要声明 Set 方法。 [DataContract]public class BookShelfDataModel{ public BookShelfDataModel() { BookList new List<BookDataModel>(); } [DataMember] public List<BookDataModel>…

sql注入语句示例大全_SQL Group By语句用示例语法解释

sql注入语句示例大全GROUP BY gives us a way to combine rows and aggregate data.GROUP BY为我们提供了一种合并行和汇总数据的方法。 The data used is from the campaign contributions data we’ve been using in some of these guides.使用的数据来自我们在其中一些指南…

ConcurrentHashMap和Collections.synchronizedMap(Map)的区别是什么?

ConcurrentHashMap和Collections.synchronizedMap(Map)的区别是什么&#xff1f; 我有一个会被多个线程同时修改的Map 在Java的API里面&#xff0c;有3种不同的实现了同步的Map实现 HashtableCollections.synchronizedMap(Map)ConcurrentHashMap 据我所知&#xff0c;HashT…

pymc3 贝叶斯线性回归_使用PyMC3估计的贝叶斯推理能力

pymc3 贝叶斯线性回归内部AI (Inside AI) If you’ve steered clear of Bayesian regression because of its complexity, this article shows how to apply simple MCMC Bayesian Inference to linear data with outliers in Python, using linear regression and Gaussian ra…

Hadoop Streaming详解

一&#xff1a; Hadoop Streaming详解 1、Streaming的作用 Hadoop Streaming框架&#xff0c;最大的好处是&#xff0c;让任何语言编写的map, reduce程序能够在hadoop集群上运行&#xff1b;map/reduce程序只要遵循从标准输入stdin读&#xff0c;写出到标准输出stdout即可 其次…

mongodb分布式集群搭建手记

一、架构简介 目标 单机搭建mongodb分布式集群(副本集 分片集群)&#xff0c;演示mongodb分布式集群的安装部署、简单操作。 说明 在同一个vm启动由两个分片组成的分布式集群&#xff0c;每个分片都是一个PSS(Primary-Secondary-Secondary)模式的数据副本集&#xff1b; Confi…

归约归约冲突_JavaScript映射,归约和过滤-带有代码示例的JS数组函数

归约归约冲突Map, reduce, and filter are all array methods in JavaScript. Each one will iterate over an array and perform a transformation or computation. Each will return a new array based on the result of the function. In this article, you will learn why …

为什么Java里面的静态方法不能是抽象的

为什么Java里面的静态方法不能是抽象的&#xff1f; 问题是为什么Java里面不能定义一个抽象的静态方法&#xff1f;例如&#xff1a; abstract class foo {abstract void bar( ); // <-- this is okabstract static void bar2(); //<-- this isnt why? }回答一 因为抽…