线程间的通信

线程是操作系统调度的最小单位,有自己的栈空间,可以按照既定的代码逐步执行,但是如果每个线程都孤立的运行,就会造成资源的浪费.如果在现实中需要多个线程按照指定的规则共同完成一件任务,那么线程间就需要互相协调,这个过程被称为线程的通信.

线程通信的定义

当多个线程共同操作共享资源时,线程间通过某种方式互相告知自己的状态,以免无效的资源争夺.

线程间的通信方式

1等待-通知 2共享内存 3管道流

低效的线程轮询
public synchronized void add(T element) throws Exception
    {
        if (amount.get() > MAX_AMOUNT)
        {
            Print.tcfo("队列已经满了!");
            return;
        }
       ...
    }

看这个伪代码,不管缓冲区是否满了都需要进行一次判断.

 public synchronized T fetch() throws Exception
    {
        if (amount.get() <= 0)
        {
            Print.tcfo("队列已经空了!");
            return null;
        }
        ...
    }

当缓冲区为空时,无法取出数据时还是要在判断一次.

针对上述情况可以通过等待-通知方式进行生产者-消费者之间的线程通信.

具体实现

数据缓冲区满的时候,可以让生产者等待,等到缓冲区可以放入数据的时候唤醒生产者线程.

数据缓冲区空的时候,可以让消费等待,等到缓冲区可以消费数据的时候唤醒消费者线程.

可以由消费者取出数据的时候去唤醒等待的生产者线程.

可以由生产者放入数据的时候去唤醒等待的消费者线程.

java语言中等待-通知方式的线程间通信使用对象的wait() notify()两类方法实现.

wait方法和notify方法的原理

1对象的wait()方法

对象的wait()方法主要作用就是让当前线程阻塞并且被等待唤醒.wait()方法与对象的监视锁密切相关,使用wait()方法也要放到同步块中.

synchronized (lock){
            //同步保护的代码块
            lock.wait();
            ...
        }

Object类中wait方法有三个版本

public final native void wait(long timeout) throws InterruptedException;

这是一个基础版本,当线程调用了同步对象的wait方法后,当前线程会进入等待被唤醒状态.

public final native void wait(long timeout) throws InterruptedException;

这是一个限时等待状态,当线程调用了同步对象的这个方法后,当前线程会等待其他线程唤醒或者指定时间结束,不在被阻塞.

public final void wait(long timeout, int nanos) throws InterruptedException 

这是一个高精度的等待版本,主要作用是更精确的控制等待时间.

2:wait方法的核心原理

当线程调用了某个锁对象的wait方法后,JVM会将当前线程加入锁对象监视器WaitSet(等待集)被其他线程唤醒.

当前线程会释放监视器的拥有权,让其他线程可以抢锁对象的监视器.

让当前线程等待,状态变成WAITING.

3:对象的notify方法

对象的notify方法主要是唤醒等待中的线程,notify方法与对象的监视锁密切相关,使用notify()方法也要放到同步块中.

synchronized (lock){
            //同步保护的代码块
            lock.notify();
            ...
        }

notify方法有两个版本

  public final native void notify();

锁对象的notify方法被调用后,唤醒锁对象监视器等待集合中的第一个等待线程.被唤醒的线程进入EntryList.状态从WAITING等待状态进入BLOCKED.

public final native void notifyAll();

锁对象的notifyAll方法被调用后,唤醒锁对象监视器等待集合中所有的等待线程.所有被唤醒的线程进入EntryList.状态从WAITING等待状态进入BLOCKED.

4:notify方法的核心原理

当前线程调用了锁对象的notify方法后,JVM会唤醒锁对象监视器WaitSet中的第一个线程.

当前线程调用了锁对象的notifyAll方法后,JVM会唤醒锁对象监视器WaitSet中的所有线程.

等待线程被唤醒后,会从监视器的WaitSet移动到EntryList中,线程具备了排队抢夺监视器的资格.状态从WAITING等待状态进入BLOCKED.

EntryList中的线程抢夺到监视器权利后,线程的状态从BLOCKED变成Runnable,具备重新执行的资格.

等待-通知通信演示

public class WaitNotifyTest {

    static Object lock = new Object();

    //等待线程的异步任务.
    static class WaitTarget implements Runnable {

        @Override
        public void run() {
            //加锁.
            synchronized (lock) {
                System.out.println("启动等待");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("收到通知继续执行");
            }
        }
    }

    //等待线程的异步任务.
    static class NotifyTarget implements Runnable {

        @Override
        public void run() {
            //加锁.
            synchronized (lock) {
                System.out.println("开始唤醒");
                lock.notifyAll();
                System.out.println("发出通知了,但是线程还没有立马释放锁");
            }
        }

        public static void main(String[] args) throws InterruptedException {
            Thread waitThread = new Thread(new WaitTarget(), "等待线程");
            waitThread.start();
            Thread.sleep(1000);
            Thread notifyThread = new Thread(new NotifyTarget(), "等待线程");
            notifyThread.start();
        }
    }
}

很喜欢带上耳机的感觉呀,因为你就是我的全世界.

如果大家喜欢我的分享的话,可以关注下我的微信公众号

心有九月星辰

你可能感兴趣的:(java,jvm,开发语言)