zoukankan      html  css  js  c++  java
  • 线程之间为什么要通信

    线程之间为什么要通信?

    通信的目的是为了更好的协作,线程无论是交替式执行,还是接力式执行,都需要进行通信告知。那么java线程是如何通信的呢,大致有以下四种方式。

    Java线程的通信方式

    1. volatile
    2. 等待/通知机制
    3. join方式
    4. threadLocal
      volatile关键字方式

    volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。

    volatile语义保证线程可见性有两个原则保证

    • 所有volatile修饰的变量一旦被某个线程更改,必须立即刷新到主内存
    • 所有volatile修饰的变量在使用之前必须重新读取主内存的值

    volatile保证可见性原理图

    img

    工作内存2能够感知到工作内存1更新a值是靠的总线,工作内存1在将值刷新的主内存时必须经过总线,总线就能告知其他线程有值被改变,那么其他线程就会主动读取主内存的值来更新。

    示例

    /**
     *
     * @author wengyz
     * @version VolatileDemo.java, v 0.1 2020-04-12 22:09
     */
    public class VolatileDemo {
    
        private static volatile boolean flag = true;
    
        public static void main(String[] args) {
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true){
                        if (flag){
                            System.out.println("trun on");
                            flag = false;
                        }
                    }
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true){
                        if (!flag){
                            System.out.println("trun off");
                            flag = true;
                        }
                    }
                }
            }).start();
        }
    }
    

    录屏演示

    如果将volatile关键字去掉,线程切换一定次数后将不能感知到flag的变化,最开始能感知是线程启动时间差的原因。

    等待/通知机制

    等待通知机制是基于wait和notify方法来实现的,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被通知或者被唤醒。

    为什么要必须获取锁?
    因为调用wait方法时,必须要先释放锁,如果没有持有锁将会抛出异常。

    代码示例

    /**
     *
     * @author wengyz
     * @version WaitDemo.java, v 0.1 2020-04-12 22:42
     */
    public class WaitDemo {
    
        private static Object lock = new Object();
        private static  boolean flag = true;
    
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock){
                        while (flag){
                            try {
                                System.out.println("wait start .......");
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
    
                        System.out.println("wait end ....... ");
                    }
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (flag){
                        synchronized (lock){
                            if (flag){
                                lock.notify();
                                System.out.println("notify .......");
                                flag = false;
                            }
    
                        }
                    }
                }
            }).start();
        }
    }
    

    wait流程原理图

    img

    join方式

    join其实合理理解成是线程合并,当在一个线程调用另一个线程的join方法时,当前线程阻塞等待被调用join方法的线程执行完毕才能继续执行,所以join的好处能够保证线程的执行顺序,但是如果调用线程的join方法其实已经失去了并行的意义,虽然存在多个线程,但是本质上还是串行的,最后join的实现其实是基于等待通知机制的。

    img

    threadLocal方式

    threadLocal方式的线程通信,不像以上三种方式是多个线程之间的通信,它更像是一个线程内部的通信,将当前线程和一个map绑定,在当前线程内可以任意存取数据,减省了方法调用间参数的传递。

    来源公众号:有意思的程序员

  • 相关阅读:
    将图片部署在tomcat/iportWork/uploadFiles中
    idea中的svn配置
    mavan和idea的搭建,很好的一篇文章
    C#如何遍历数组?
    java 泛型小小的测试题
    js关于变量作为if条件的真假问题
    HTML5实现两个视频循环播放!
    JQuery利用选择器定位动态id?
    hibernate QBC查询
    mybatis前台传来一个String,后后台执行sql变成了true
  • 原文地址:https://www.cnblogs.com/tangliMeiMei/p/15480559.html
Copyright © 2011-2022 走看看