线程通信

来源:互联网 发布:java 工作流设计器 编辑:程序博客网 时间:2024/06/06 16:49
     本文介绍线程通信的两种方式,一种是只有以synchronized方式实现线程同步时,可以采取的线程通信方式;另外一种是以ReentrantLock实现线程同步时,可以采取的线程通信方式。以下我们将分别介绍这两种实现线程通信的方式。
一、采用synchronized保证线程同步时如何实现线程通信
     java Object类实现了wait(),notify(),notifyAll()三种方法,这三种方法是实现线程通信的关键所在。并且这三种方法必须由同步监视器(所谓同步监视器就是个锁,也就是synchronized(锁))调用,如果synchronized锁的对象和实现了wait(),notify(),notifyAll()这三种方法的对象不是一个的话,将会抛出java.lang.IllegalMonitorStateException异常。接下来我们了解下这三个方法是作用。
  • wait():导致当前线程等待,直到其他线程调用该同步监视器(即锁)的notify()或notifyAll()方法时来唤醒该线程。同时调用wait()方法的当前线程会释放对该同步监视器的锁定(翻译成人话就是:当调用wait方法时,会释放这个同步锁,也就是synchronized锁定的那个对象,也就是调用wait()方法的对象)。
  • notify():调用notify()方法时,会唤醒由于阻塞在同一个同步监视器调用了wait()方法的线程,翻译成书面语就是,唤醒在此同步监视器上等待的当个线程,如果在此同步监视器上阻塞着很多线程,则会选择一个唤醒,至于唤醒那个线程,这基于JVM对线程的调度。
  • notifyAll():唤醒在此同步监视器上阻塞的所有线程。也就是唤醒所有线程由于调用了该同步监视器的await方法而导致阻塞。
编程注意事项:
       wait(),notify(),notifyAll()这三种方法的调用必须在synchronized方法或者块中。
Demo:
代码:
public class TestNotify {
    // 共享资源
    private static int num;
    // 同步监视器
    private static Object obj = new Object();

    static class produce extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (obj) {
                    try {
                        if (num == 5) {
                            System.out.println("num == 5 produce await");
                            obj.wait();
                        }
                        num++;
                        System.out.println("produce thread num=" + num);
                        obj.notifyAll();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    static class consumer extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (obj) {
                    try {
                        if (num == 0) {
                            System.out.println("num == 0 consumer await");
                            obj.wait();
                        }
                        num--;
                        System.out.println("consumer thread num=" + num);
                        obj.notifyAll();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        new produce().start();
        new consumer().start();

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.exit(0);

    }

}
运行结果:
produce thread num=1
produce thread num=2
produce thread num=3
produce thread num=4
produce thread num=5
num == 5 produce await
consumer thread num=4
consumer thread num=3
consumer thread num=2
produce thread num=3
produce thread num=4
produce thread num=5
num == 5 produce await
consumer thread num=4
produce thread num=5
num == 5 produce await
consumer thread num=4
produce thread num=5

二、采用ReentrantLock保证线程同步如何实现线程通信
    由于采用Object作为同步监视器时,必须以synchronized的方式实现线程同步,但由于实际项目开发中,使用可重入锁ReentrantLock保证线程同步的方式比较多,在这种情况下就需要使用别的方式实现线程通信。java1.5提供了Condition对象来实现以Lock方式实现的线程同步时如何实现线程通信。condition替代了传统Object的wait(),notify(),notifyAll()方法,取而代之的是await(),signal(),signalAll()方法。这种方式可以具有多种等待结果集(我的理解就是同一把锁,可以new 多个Condition,每种Condition调用await()方法都可以导致当前线程等待);接下来一次介绍这三种方法。
  • await():对应于Object的wait()方法,可以导致当前线程阻塞,直到其他线程调用了同一个Condition的signal()或signalAll()方法来唤醒当前线程。同时调用await()方法的当前线程会释放同步监视器(即:锁,该Condition对应的那把锁,也就是用于保证线程同步的那把锁)
  • signal():用于唤醒相同Condition导致阻塞的其中一个线程,如果该Condition阻塞了多个线程,则选择一个线程唤醒。
  • signalAll():用于唤醒相同Codition导致阻塞的所有线程。
编程注意事项:
  1. await(),signal(),signalAll()三种方法的调用必须在lock.lock()和lock.unlock()方法之间,说白了就是必须在线程具有相同同步监视器(锁)时,才可以调用这三种方法。
  2. 调用这三种方法的Condition对应的Lock 和保证线程同步的锁必须是同一个,否则会抛出java.lang.IllegalMonitorStateException异常。
Demo:
code:
public class TestCondition 
{
    private static ReentrantLock lock1 = new ReentrantLock();

    private static ReentrantLock lock2 = new ReentrantLock();

    private static int num;

    static Condition condition  = lock1.newCondition();

    static Condition condition2  = lock1.newCondition();

    public static void main(String[] args) {

        new produce().start();
        new consumer().start();

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.exit(0);
    }

    static class produce extends Thread
    {
        @Override
        public void run() 
        {
            while(true)
            {
            // 如果保证线程同步的锁是lock2,则会java.lang.IllegalMonitorStateException异常
            lock1.lock();
            try
            {
                if (num == 5)
                {
                    System.out.println("num == 5 produce await");
                    // 如果是condition2 则线程会一直阻塞下去,因为没有线程调用condition2的signal方法
                    condition.await();
                }
                num++;
                System.out.println("produce thread num=" + num);
                condition.signalAll();

            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                lock1.unlock();
            }
            }
        }
    }

    static class consumer extends Thread
    {
        @Override
        public void run() 
        {
            while(true)
            {
            lock1.lock();
            try
            {
                if (num == 0)
                {
                    System.out.println("num == 0 consumer await");
                    condition.await();
                }

                num--;
                System.out.println("consumer thread num="+num);
                condition.signalAll();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                lock1.unlock();
            }
            }
        }
    }

}
运行结果:
produce thread num=1
produce thread num=2
produce thread num=3
produce thread num=4
produce thread num=5
num == 5 produce await
consumer thread num=4
consumer thread num=3
consumer thread num=2
consumer thread num=1
consumer thread num=0
num == 0 consumer await

三、线程通信的应用
       线程通信是建立在多线程的基础上,没有多线程也无从谈起线程之间的通信;多线程最经典的模型是生产-消费者模型,试想一个共享资源队列,生产者向队列中添加,消费者从队列中移出,如果队列满了,则生产者必须等待消费者将商品从队列中移出,这样才能继续生产,但是在生产者等待的这段时间,必须释放掉对共享资源(即:队列)的占有权(锁),这样消费者才能拿到队列,并从队列中取出商品。所以这时就需要生产者和消费者之间要有通信,才能保证系统的更好的运转。
      其实在编写上述小事例的代码时,完全可以通过阻塞队列来达到目的,当队列为空时,如果还从队列中取(take())则当前线程会一直阻塞,直到队列有数据;如果队列已满,还往队列中加(put())则当前线程阻塞。直到队列有空间。阻塞队列之所以能够实现,是因为阻塞队列的内部也是采用了线程通信的方式。
0 0
原创粉丝点击