本文共 5031 字,大约阅读时间需要 16 分钟。
今天小咸儿来讲解一个好玩的事,那就是线程之间该如何通信,线程通信之后又会出现什么问题?
先来一张导图来看看线程通讯的分布?
疑问:如果想要线程按照用户自定义的顺序执行的话,那该如何操作呢?
思考:如果能够让线程等待先执行的线程执行完,再执行不就能达到效果了吗!?
果然出现问题之后,就会有对应的解决之法,接下来先看一个简单而且有趣的方法——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的线程结束后,则开始执行。这个方法看到名字就会很清楚是干什么用的了,就是表面意思,让线程睡一会。
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()静态方法和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方法的区别:
重点来了,等待唤醒机制是什么,在多线程执行过程中,在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/