线程间的通信

 一、概述

       每个线程都有自己的栈空间,他们运行过程即按照既定的代码一步步执行指到结束。如果每个线程都是孤立的运行,互相不影响那么这就使得线程的价值大大减少,使用多线程当然是希望能够利用多线程互相配合完成工作,这样才能体现出多线程的巨大价值。如何互相配合完成工作,这就涉及到线程之间的通信。

二、几个重要的方法

       线程之间的通信涉及到几个重要的方法,具体如下,下面几个方法是被定义在Object和Thread类中。

1)Object--->wait

      当调用此方法,会使当前线程处于WAITING状态,只有等待另外线程的通知或被中断,才会返回执行,需要注意的是当线程处于WAITING状态时,会释放对象锁。

      该方法有几个重写的方法,例如wait(long timeout), 意思是超时等待一段时间,如果没有通知则会自动返回执行。

2)Object--->notify

通知一个在对象上等待的线程,使其从wait方法返回,但前提是等待的线程必须获得了对象锁,也就是说当前没有其他线程占用同一个对象锁。

3)Object--->notifyAll
看方法名就知道跟notify很相似,它是通知所有等待在该对象上的线程。

4)Thread--->sleep
与wait类似的还有一个方法sleep,该方法属于Thread类中,该方法是让当前线程处于睡眠状态,在睡眠过程中,改线程不会释放资源也就是说不会释放锁。

下面通过一个实例来具体讲述以上方法的使用。

public class WaitNotify {

    static boolean flag = true;
    static Object lock = new Object();


    public static void main(String []args){
        Thread waitThread1 = new Thread(new Wait(), "WaitThread---1");
        waitThread1.start();
        Thread waitThread2 = new Thread(new Wait(), "WaitThread---2");
        waitThread2.start();
        SleepUtils.second(1);
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();
    }

    static class Wait implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread()+" run...");
            synchronized (lock){
                while (flag){
                    System.out.println(Thread.currentThread()+ " flag is true, start wait "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(Thread.currentThread()+ " flag is false, wait end "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

    static class Notify implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread()+" run...");
            synchronized (lock){
                System.out.println(Thread.currentThread()+ " hold lock, nofity  "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notify();  // 此处只会触发等待队列中的一个线程执行
                flag = false;
                SleepUtils.second(5);
                System.out.println(Thread.currentThread()+ " hold lock, nofity  sleep end"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
            SleepUtils.second(2);
            synchronized (lock){
                System.out.println(Thread.currentThread()+ " hold lock again, nofity "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
                SleepUtils.second(5);
                System.out.println(Thread.currentThread()+ " hold lock again, nofity sleep end"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

}

SleepUtils类的实现请查看前一篇文章 Java线程的几种状

执行如上代码,输出如下:

1、Thread[WaitThread---1,5,main] run...
2、Thread[WaitThread---2,5,main] run...
3、Thread[WaitThread---1,5,main] flag is true, start wait 21:28:25
4、Thread[WaitThread---2,5,main] flag is true, start wait 21:28:25
5、Thread[NotifyThread,5,main] run...
6、Thread[NotifyThread,5,main] hold lock, nofity  21:28:26
7、Thread[NotifyThread,5,main] hold lock, nofity  sleep end21:28:31
8、Thread[WaitThread---1,5,main] flag is false, wait end 21:28:31
9、Thread[NotifyThread,5,main] hold lock again, nofity 21:28:33
10、Thread[NotifyThread,5,main] hold lock again, nofity sleep end21:28:38
通过 3、4行输出可以知道,wait只是让线程处于等待状态,并会释放相关对象锁。

通过6、7、8行输出可以知道,sleep方法只是让当前线程处于睡眠状态,并且不会释放对象锁,同时notify方法只会触发等待队列中某一个线程执行。

如果我们把lock.notify() 修改为 lock.notifyAll(),会是怎样的输出呢?

Thread[WaitThread---1,5,main] run...
Thread[WaitThread---2,5,main] run...
Thread[WaitThread---1,5,main] flag is true, start wait 21:37:33
Thread[WaitThread---2,5,main] flag is true, start wait 21:37:33
Thread[NotifyThread,5,main] run...
Thread[NotifyThread,5,main] hold lock, nofity  21:37:34
Thread[NotifyThread,5,main] hold lock, nofity  sleep end21:37:39
Thread[WaitThread---2,5,main] flag is false, wait end 21:37:39
Thread[WaitThread---1,5,main] flag is false, wait end 21:37:39
Thread[NotifyThread,5,main] hold lock again, nofity 21:37:41
Thread[NotifyThread,5,main] hold lock again, nofity sleep end21:37:46

我们发现WaitThread---1和2都执行到wait end 结束。

5)Thread--->join

Thread类中还有一个方法即:join方法,其含义是当前线程等待thread线程终止之后才开始执行,很类似于wait,join方法也有几个重写方法,join(long millis)等等。

下面看一个具体事例:

public class Join {

    public static void main(String []args){
        Thread root = Thread.currentThread();

        Thread thread1 = new Thread(new JoinThread(root), "thread--1");
        Thread thread2 = new Thread(new JoinThread(thread1), "thread--2");

        thread1.start();
        thread2.start();

        SleepUtils.second(5);

        System.out.println(root+"  end...");

    }

    static class JoinThread implements Runnable{

        private Thread mThread;

        public JoinThread(Thread mThread) {
            this.mThread = mThread;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread()+" before join...");
            try {
                this.mThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+" end join...");
        }
    }

}

输出如下:

Thread[thread--1,5,main] before join...
Thread[thread--2,5,main] before join...
Thread[main,5,main]  end...
Thread[thread--1,5,main] end join...
Thread[thread--2,5,main] end join...
当main线程运行结束后, thread--1线程开始运行,thread--1运行结束在到thread--2运行。

为什么join方法会有这样的效果,看一下join的具体实现,可以发现join内部其实是由wait方法实现的,看到wait方法我们知道需要在该对象上执行notify方法才能唤醒该对象上的线程队列。这里涉及到一个未知的细节:当线程终止是,会调用线程自身的notifyAll方法,通知所有等待在该线程对象上的线程。

总结

1、wait()方法、 notify()、notifyAll() 这三个方法都属于Object类中的方法;
2、sleep()、join()为Thread类中的方法;
3、wait()方法使得当前线程处于等待状态,等到同一对象上的其他线程调用notify()或者notifyAll()方法才会唤醒;
4、wait()方和Thread中的sleep方法不一样,wait它不会占用锁,执行wait方法会释放当前锁,而sleep却会不;

5、join() 的作用:让当前线程等待thread线程结束之后才能继续运行。


参考文献

《Java并发编程的艺术》



猜你喜欢

转载自blog.csdn.net/u010349644/article/details/80738520