美文网首页
线程间通信

线程间通信

作者: 沐小晨曦 | 来源:发表于2018-11-18 20:23 被阅读10次

    概述

    线程间的通信有以下几种方式:

    1. volatile 和 synchronized 关键字
    2. 等待 / 通知机制
    3. 管道输入/ 输出流
    4. Thread.join() 的使用
    5. ThreadLocal

    volatile 与 synchronized

    volatile 与 synchronized 保证可见性,可见性如果用内存模型来阐述,那就是在变量的写的时候,把把该变量同步到主内存,其他线程通过嗅探主内存的该变量值是否变化,在变化的时候更新自己的工作内存,这样就完成了线程之间的交互。

    等待 / 通知机制

    等待 / 通知的相关方法是任意 Java 对象都具备的,因为这些方法被定义在所有对象的超类 java.lang.Object 上:

    方法名称 描述
    notify() 通知一个在对象上等待的线程,使其从 wait() 方法返回,而返回的前提是该线程获取到了对象的锁
    notifyAll() 通知所有等待在该对象上的线程
    wait() 调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断才会返回,需要注意,调用 wait() 方法后,会释放对象的锁
    wait(long) 超时等待一段时间,这里的参数时间单位是毫秒,如果没有通知就超时返回

    等待 / 通知机制,是指一个线程 A 调用了对象 O 的 wait 方法进入等待状态,而另外一个线程 B 调用了对象 O 的 notify 或者 notifyAll 方法,线程 A 收到通知后从对象 O 的 wait 方法返回,进而执行后续操作。上诉两个线程通过对象 O 完成交互,而对象上的 wait 方法和 notify / notifyAll 的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。

    在使用以上方法需要注意几点:

    1. 使用 wait、notify、notifyAll 时需要先对调用对象加锁
    2. 调用 wait 方法后,线程状态由 RUNNING 变为 WAITING,并将当前线程放置到对象的等待队列
    3. notify 或 notifyAll 方法调用之后,等待线程依旧不会从 wait 返回,需要调用 notify 或 notifyAll 的线程释放锁之后,等待线程才有机会从 wait 方法
    4. notify 方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而 notifyAll 方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由 WAITING 变为 BLOCKED
    5. 从 wait 方法返回的前提是获得了调用对象的锁

    管道输入 / 输出流

    管道输入 / 输出流和普通的文件输入 / 输出流或者网络输入 / 输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。

    管道输入 / 输出流主要包括了如下四种具体实现:PipedOutputStream、PipedInputStream、PipedReader、PipedWriter,前两种面向字节,而后两种面向字符。

    public class PipedDemo {
        public static void main(String[] args) throws IOException {
            PipedReader pipedReader = new PipedReader();
            PipedWriter pipedWriter = new PipedWriter();
            //将输入输出流进行链接,否则会抛出 IOException
            pipedWriter.connect(pipedReader);
            Thread printThread = new Thread(new PrintRunnable(pipedReader));
            printThread.start();
            int receive = 0;
            try {
                while ((receive = System.in.read()) != -1) {
                    pipedWriter.write(receive);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                pipedWriter.close();
            }
        }
    
        static class PrintRunnable implements Runnable {
            private PipedReader pipedReader;
    
            public PrintRunnable(PipedReader pipedReader) {
                this.pipedReader = pipedReader;
            }
    
            @Override
            public void run() {
                int receive = 0;
                try {
                    while ((receive = pipedReader.read()) != -1) {
                        System.out.println("输出:" + (char) receive);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * Demo
     * 输出:D
     * 输出:e
     * 输出:m
     * 输出:o
     * 输出
     */
    

    Thread.join() 的使用

    如果一个线程 A 执行了 thread.join() 语句,其含义是:当前线程 A 等待 thread 线程终止之后才从 thread.join() 返回。线程 Thread 除了提供 join 方法之外,还提供了 join(long millis)等两个具备超时特性的方法。这两个超时方法表示,如果线程 thread 在给定的超时时间里没有终止,那么将会从该超时方法中返回。

    public class JoinDemo {
        public static void main(String[] args) {
            Thread previous = Thread.currentThread();
            for (int i = 0; i < 10; i++) {
                //每个线程拥有前一个线程的引用,需要等待前一个线程终止,才能从等待中返回
                Thread thread=new Thread(new MyRunnable(previous),String.valueOf(i));
                thread.start();
                previous=thread;
            }
        }
    
        static class MyRunnable implements Runnable{
            private Thread thread;
    
            public MyRunnable(Thread thread) {
                this.thread = thread;
            }
    
            @Override
            public void run() {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" terminate ");
            }
        }
    }
        /**
         * 0 terminate 
         * 1 terminate 
         * 2 terminate 
         * 3 terminate 
         * 4 terminate 
         * 5 terminate 
         * 6 terminate 
         * 7 terminate 
         * 8 terminate 
         * 9 terminate
         */
    

    从输出可以看出,每个线程终止的前提是前驱线程的终止,每个线程等待前驱线程终止后,才从 join 方法返回,这里涉及了等待 / 通知记住(等待前驱线程结束,接受前驱线程结束的通知)。

    当线程终止时,会调用线程自身的 notifyAll 方法,会通知所有等待在该线程对象上的线程。

    ThreadLocal 的使用

    ThreadLocal,即线程变量,是一个以 ThreadLocal 对象为键、任意对象为值的存储结构。这个结构被依附在线程上,也就是说一个线程可以根据一个 ThreadLocal 对象查询到绑定在这个线程上的一个值。

    可以通过 set(T) 方法来设置一个值,在当前线程下再通过 get() 方法获取到原先设置的值。

    public class ThreadLocalDemo {
    
        private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<>() {
            @Override
            protected Long initialValue() {
                return System.currentTimeMillis();
            }
        };
    
        public static final void begin() {
            TIME_THREADLOCAL.set(System.currentTimeMillis());
        }
    
        public static final long end() {
            return System.currentTimeMillis() - TIME_THREADLOCAL.get();
        }
    
        public static void main(String[] args) throws InterruptedException {
            ThreadLocalDemo.begin();
            Thread.sleep(2000);
            System.out.println(ThreadLocalDemo.end());
        }
    }
    //输出 2003
    

    相关文章

      网友评论

          本文标题:线程间通信

          本文链接:https://www.haomeiwen.com/subject/pstnfqtx.html