博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程——线程通讯
阅读量:2492 次
发布时间:2019-05-11

本文共 5031 字,大约阅读时间需要 16 分钟。

文章目录

前言

  今天小咸儿来讲解一个好玩的事,那就是线程之间该如何通信,线程通信之后又会出现什么问题?


叙述

宝图

  先来一张导图来看看线程通讯的分布?

在这里插入图片描述

Join 线程

  疑问:如果想要线程按照用户自定义的顺序执行的话,那该如何操作呢?

  思考:如果能够让线程等待先执行的线程执行完,再执行不就能达到效果了吗!?

  果然出现问题之后,就会有对应的解决之法,接下来先看一个简单而且有趣的方法——join

Thread 提供了让一个线程等待另一个线程完成的方法——join()方法。

代码展示:

package com.practice.demo.thread;/** * 线程的第三个实例,继承Thread类 * 测试join()方法 * @author Phyllis * @date 2019年7月2日17:26:23 */public class JoinThread extends Thread{
/** * 提供一个有参数的构造器,用于设置该线程的名字 * @param name 线程的名字 */ public JoinThread(String name){
super(name); } /** * 重写run()方法,定义线程执行体 */ @Override public void run() {
for (int i = 0; i<100; i++){
System.out.println(getName()+" "+ i); } } public static void main(String[] args) throws Exception{
// 启动子线程 new JoinThread("新线程").start(); for (int i = 0; i<100; i++){
if (i == 20){
JoinThread jt = new JoinThread("被Join的线程"); jt.start(); // main线程调用了jt线程的join()方法,main线程 // 必须等待jt执行结束才会向下执行 jt.join(); } System.out.println(Thread.currentThread().getName()+" "+i); } }}

打印结果:

在这里插入图片描述
在这里插入图片描述
  结果: 当i=20时,被join的线程开始执行,这时,main线程一直处于阻塞的状态,所以一直在等待,等到join的线程结束后,则开始执行。

sleep

  这个方法看到名字就会很清楚是干什么用的了,就是表面意思,让线程睡一会。

synchronized (res){
try {
if (!res.flag){
res.wait(); } Thread.sleep(1000); }catch (Exception e){
} System.out.println(res.name + "," + res.sex); res.flag = false; res.notify();}

如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过Thread类的静态sleep()方法来实现。

  与之类似的方法还有一个yield()方法

yield

  yield()静态方法和sleep()方法类似,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态。

synchronized (res){
try {
if (!res.flag){
res.wait(); } Thread.yield(); }catch (Exception e){
} System.out.println(res.name + "," + res.sex); res.flag = false; res.notify();}

缺点: yield方法只是让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield方法暂停之后,线程调度器又再次调度到了该线程。

  sleep方法和yield方法的区别:

  • sleep方法暂停当前线程后,会给其他线程执行的机会,不会理会其他线程的优先级;但是yield方法只会给优先级相同或者更高级别的线程机会。(看来yield还看高低区分的哼)
  • sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态;而yield不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。因此有可能在暂停后,因获得处理器资源而再次执行。
  • sleep方法声明抛出了InterruptException异常,所以调用sleep方法时要么捕捉该异常,要不显示声明抛出该异常;而yield方法则没有声明抛出任何异常。
  • sleep方法比yield方法有更好的可移植性。

等待唤醒机制

  重点来了,等待唤醒机制是什么,在多线程执行过程中,在A线程对共享变量,进行操作时,B线程要等待。A线程结束操作时,则唤醒B线程

在这里插入图片描述

示例:生产与消费

package com.practice.demo.thread;/** * 共享对象 * @author Phyllis * @date 2019年7月12日09:15:26 */class Res{
/** * 姓名 */ public String name; /** * 性别 */ public String sex; /** * 为true的情况下,允许读,不能写 * 为false的情况下,允许写,不能读 */ public boolean flag = false;}/** * 生产这线程 * @author Phyllis * @date 2019年7月12日09:16:55 */class InThread extends Thread{
public Res res; public InThread(Res res){
this.res = res; } @Override public void run() {
// count 为0 或者 1 int count = 0; while (true){
synchronized (res){
if (res.flag){
try {
// 释放当前锁对象,当前线程等待 res.wait(); }catch (Exception e){
} } if (count == 0){
res.name = "小红"; res.sex = "女"; } else{
res.name = "小军"; res.sex = "男"; } // 0 1 0 1 0 1 0 1 count = (count + 1) % 2; // 标记当前线程为等待 res.flag = true; // 唤醒被等待的线程 res.notify(); } } }}/** * 消费这线程,读 * @author Phyllis * @date 2019年7月12日09:16:55 */class OutThread extends Thread {
public Res res; public OutThread(Res res){
this.res = res; } @Override public void run() {
while (true){
synchronized (res){
try {
if (!res.flag){
res.wait(); } Thread.sleep(1000); }catch (Exception e){
} System.out.println(res.name + "," + res.sex); res.flag = false; res.notify(); } } }}/** * 客户端 * @author Phyllis * @date 2019年7月12日09:16:55 */public class ThirdThread{
public synchronized static void main(String[] args) throws InterruptedException {
Res res = new Res(); InThread inThread = new InThread(res); OutThread outThread = new OutThread(res); inThread.start(); outThread.start(); }}

打印结果:

在这里插入图片描述
  所以使用等待唤醒机制来保证生产一个,消费一个。

注意: wait和notify方法,一定要在synchronized中进行,持有同一把锁。

区别

  wait和join的区别:

  wait需要被唤醒。

  wait和sleep的区别:

  sleep不会释放锁。

问题

  在宝图中也可以明显的看出,小咸儿在通讯那里打了两个问号,也就是说通讯有可能会导致线程安全的问题,那么线程安全是什么呢?又该如何解决呢?且听小咸儿下次分享。


总结

  多线程是十分实用并且常用的内容,接下来小咸儿还会继续深入学习多线程,更多的内容等待更新。

感谢您的阅读~~

转载地址:http://ijerb.baihongyu.com/

你可能感兴趣的文章
201521123061 《Java程序设计》第十一周学习总结
查看>>
代码小思考
查看>>
【NOI 2018】归程(Kruskal重构树)
查看>>
希赛软件设计师视频教程-3.1 进程(第三部分) 标清
查看>>
配置springboot在访问404时自定义返回结果以及统一异常处理
查看>>
JS字符串与二进制的相互转化
查看>>
linux ubunt 安装软件的前期准备——更新源的更换
查看>>
C++语言 填充选区
查看>>
1.Rabbitmq学习记录《本质介绍,协议AMQP分析》
查看>>
mysql安装与基本使用
查看>>
记一次Hbase数据迁移和遇到的问题
查看>>
Axure原型制作规范
查看>>
【UOJ#246】套路(动态规划)
查看>>
Repeater + 分页控件 AspNetPager 研究
查看>>
eclipse中的汉字极小的解决方案(转载)
查看>>
Finereport集群配置
查看>>
Introspector内省和反射的区别.
查看>>
php-fpm配置笔记
查看>>
[转]解读ASP.NET 5 & MVC6系列(7):依赖注入
查看>>
[转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)...
查看>>