多线程——线程通讯

本文介绍了Java多线程中线程通信的概念,包括Join线程、sleep方法、yield方法以及等待唤醒机制。通过示例展示了线程间的交互方式,如使用join()实现线程顺序执行,sleep()和yield()的差异,以及wait和notify配合实现生产者消费者模型。文章还提出了线程通信可能引发的线程安全问题,并预告了后续关于线程安全的讨论。
摘要由CSDN通过智能技术生成

前言

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


叙述

宝图

  先来一张导图来看看线程通讯的分布?
在这里插入图片描述

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不会释放锁。

问题

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


总结

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

感谢您的阅读~~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值