Java并发编程-线程协作方式

本文详细介绍了Java中object的wait、notify、notifyAll方法以及Condition接口的await、signal、signalAll方法在多线程同步中的应用。通过生产者消费者模型展示了这两种方式的实现,并分析了它们的区别。wait/notify适用于简单的线程协作,而Condition提供了更灵活的等待队列管理,允许创建多个条件等待队列,适合更复杂的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、object - wait、notify、notifyAll

wait、notify、notifyAll是object中的方法,final修饰,因此不能被重写。

public final native void notify();

public final native void notifyAll();

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

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

public final void wait() throws InterruptedException {
        wait(0);
}

wait、notify、notifyAll三个方法调用的前提是已经获取到当前实例对象锁,也就是在synchroized作用范围下。

1.1、wait

1、wait的作用是让当前获取了对象锁的线程释放对象锁,并进入阻塞状态,除非发生下列四种中的某一种情况,否则线程会一直在处于阻塞状态。
(1)有其他线程调用了object的notify方法来唤醒线程;
(2)有其他线程调用了object的notifyAll方法来唤醒线程;
(3)有其他线程调用了被阻塞线程thread的interrupt方法来唤醒被阻塞线程。
(4)wait方法超时,该线程继续运行。

线程被重新唤醒后,wait后面的代码不会继续执行,线程会重新获取对象锁进入synchronized代码块。

2、如果当前线程没有持有对象锁的情况下,调用了object的wait,会抛出IllegalMonitorStateException异常。

3、当前线程调用object的wait后,如果被中断的话,InterruptedException会抛出。

1.2、notify

notify的作用是唤醒一个等待object对象锁的线程,待当前线程退出synchronized代码块释放对象锁后,被唤醒的线程才能获得对象锁。如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;

如果当前线程并没有持有object的对象锁,但调用了object的notify,会抛出IllegalMonitorStateException异常。

1.3、notifyAll

notifyAll的作用是唤醒所有正在等待对象锁的线程,待当前线程退出synchronized代码块释放对象锁后,被唤醒的线程才能获得对象锁。如果当前线程并没有持有object的对象锁,但调用了object的notify,会抛出IllegalMonitorStateException异常。

1.4、生产者、消费者模型

使用wait、notify实现:

public class Test {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
      
    public static void main(String[] args)  {
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();
          
        producer.start();
        consumer.start();
    }
      
    class Consumer extends Thread{
          
        @Override
        public void run() {
            consume();
        }
          
        private void consume() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == 0){
                        try {
                            System.out.println("队列空,等待数据");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.poll();          //每次移走队首元素
                    queue.notify();
                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
                }
            }
        }
    }
      
    class Producer extends Thread{
          
        @Override
        public void run() {
            produce();
        }
          
        private void produce() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("队列满,等待有空余空间");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.offer(1);        //每次插入一个元素
                    queue.notify();
                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
                }
            }
        }
    }
}

2、condition - await、signal、signalAll

wait、signal、signalAll是Condition中的方法,Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition() ,调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用。

Conditon中的await()对应Object的wait();

Condition中的signal()对应Object的notify();

Condition中的signalAll()对应Object的notifyAll()。

使用condition实现生产者、消费者模型:

public class Test {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();
     
    public static void main(String[] args)  {
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();
          
        producer.start();
        consumer.start();
    }
      
    class Consumer extends Thread{
          
        @Override
        public void run() {
            consume();
        }
          
        private void consume() {
            while(true){
                lock.lock();
                try {
                    while(queue.size() == 0){
                        try {
                            System.out.println("队列空,等待数据");
                            notEmpty.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();                //每次移走队首元素
                    notFull.signal();
                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
                } finally{
                    lock.unlock();
                }
            }
        }
    }
      
    class Producer extends Thread{
          
        @Override
        public void run() {
            produce();
        }
          
        private void produce() {
            while(true){
                lock.lock();
                try {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("队列满,等待有空余空间");
                            notFull.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.offer(1);        //每次插入一个元素
                    notEmpty.signal();
                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
                } finally{
                    lock.unlock();
                }
            }
        }
    }
}

3、object和condition两种线程协作方式比较

        object的协作方式,因为只锁定一个object对象,因此只能有一个等待队列,在实际应用时可能需要多个,比如读和写。为了这个灵活性,lock将同步控制和等待队列分离开来,lock对象负责同步控制,保证在某个时刻只有一个线程访问临界区。等待队列中存储着被阻塞的线程,每个condition对应一个等待队列,而一个lock可以创建多个condition。

举例说明如下:

线程A:

synchronized(obj){

    obj.wait(); 
    doSomething();

}

线程C:

synchronized(obj){

    obj.wait(); 

    doSomething();

}

线程B:

synchronized(obj){

    obj.notify(); 

}

假设三个线程执行顺序,线程A-->线程C-->线程B

所以问题来了:等待的线程中有A和C, B notify后只会唤醒其中一个执行,假如我们的需求是想让A线程执行,那么这种object的方式是无法控制的。用一个lock和两个condition就可以解决。

ReentrantLock lock = new ReentrantLock(true);

Condition aCondition = reentrantLock.newCondition();

Condition cCondition = reentrantLock.newCondition();

线程A:

{

lock.lock();

aCondition.await(); 
aCondition.signal();
doSomething();
lock.unlock();

}

线程C:

{

lock.lock();

cCondition.await();

doSomething();

lock.unlock();

}

线程B:

{

lock.lock();

aCondition.signal(); 

//cCondition.signal();

lock.unlock();

}

可见condition比object更灵活。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值