1010cc时时彩标准版 > 三分时时彩1010CC > Java线程间通讯和搭档的三种艺术,线程间协作的

原标题:Java线程间通讯和搭档的三种艺术,线程间协作的

浏览次数:98 时间:2019-08-15

使用Condition实现多线程之间调用(生产消费模式),condition多线程

现在计算机和智能手机都是多核处理器,为了更好地发挥设备的性能,提高应用程序的体验性,多线程是必不可少的技术。线程之间不是孤立的,它们共享进程的资源和数据,彼此之间还需要进行通信和协作,最典型的例子就是「生产者-消费者模型」。下面先介绍 wait/notify 机制和 Lock/Condition 机制,然后用两个线程交替打印奇偶数。

Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

 

  转载自:

一,object 类的wait(),notify()和notifyAll()

Java 线程类也是一个object 类,它的实例都继承自java.lang.Thread 或其子类。
wait(),notify()和notifyAll()是Object类中的方法,常用于线程之间调度。

线程无数据运行可调用wait()让线程等待,不占用CUP资源,提高CPU有效的利用率。
例如,线程 B 可以等待线程 A 的一个信号,这个信号会通知线程 B 数据已经准备好了,B可以执行业务逻辑。

线程之间调度常应用于生产与消费模式下。

利用Object 对象的wait(),notify()和notifyAll()可以实现,但JDK1.5后有更好的替代方法。

1. wait/notify

wait 和 notify 是 Object 类的两个方法,理解起来还是有些复杂的。它和多线程同步有关系,个人觉得放在 Object 类不太合理,可能是历史遗留问题吧。每个对象都有一把锁,在进入同步方法或代码块之前,当前线程需要先获取对象锁,然后才能执行同步块的代码,完成后释放对象锁。锁可以理解为唯一的凭证,有了它就能入场,而且独占所有的资源,立场就得交出去。

wait 方法的作用是使当前线程释放对象锁,并进入等待状态,不再往下执行。当其他线程调用对象的 notify/notifyAll 时,会唤醒等待的线程,等到其他线程释放锁后,被唤醒的现象将继续往下执行。notify 随机唤醒一个等待的线程,notifAll 唤醒所有等待的线程。注意:wait 和 notify 都需要在拿到对象锁的情况下调用。下面是 wait 的标准使用方法(来自 《Effective Java》一书):

synchronized  { while (condition does not hold) { obj.wait(); // release lock and reacquire on wakeup // perform action appropriate to condition }}

每个锁对象都有两个队列:就绪队列和阻塞队列。就绪队列存储了已经就绪的线程,阻塞队列存储了被阻塞的线程。当阻塞线程被唤醒后,才会进入就绪队列,然后等待 CPU 的调度;反之,当一个线程被阻塞后,就会进入阻塞队列,等待被唤醒。

举个例子,线程 A 在执行任务,它等待线程 B 做完某个操作,才能往下执行,这就可以用 wait/notify 实现。

 public void start() { new Thread(new TaskA.start(); new Thread(new TaskB.start(); } private final Object lock = new Object(); private boolean finished; private class TaskA implements Runnable { @Override public void run() { synchronized  { System.out.println("线程 A 拿到锁了,开始工作"); while (!finished) { try { System.out.println("线程 A 释放了锁,进入等待状态"); lock.wait(); System.out.println("线程 A 收到信号,继续工作"); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("线程 A 释放了锁"); } } private class TaskB implements Runnable { @Override public void run() { synchronized  { System.out.println("线程 B 拿到了锁,开始工作"); try { Thread.sleep; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("-----------------------"); System.out.println("线程 B 发信号了,完成工作"); finished = true; lock.notify(); } System.out.println("线程 B 释放了锁"); } }/* 打印:线程 A 拿到锁了,开始工作线程 A 释放了锁,进入等待状态线程 B 拿到了锁,开始工作-----------------------线程 B 发信号了,完成工作线程 B 释放了锁线程 A 收到信号,继续工作线程 A 释放了锁 */

Condition 可以看作 Object 的 wait/notify 的替代方案,同样用来实现线程间的协作。与使用 wait/notify 相比,Condition的 await/signal 更加灵活、安全和高效。Condition 是个接口,基本的方法就是 await() 和 signal()。Condition 依赖于 Lock 接口,生成一个 Condition 的代码是 lock.newCondition() 。 需要注意 Condition 的 await()/signal() 使用都必须在lock.lock() 和 lock.unlock() 之间才可以,Conditon 和 Object 的 wait/notify 有着天然的对应关系:

  • Conditon 中的 await() 对应 Object 的 wait();
  • Condition 中的 signal() 对应 Object 的 notify();
  • Condition 中的 signalAll() 对应 Object 的 notifyAll();

举个例子,使用 Condition 实现和上面的功能。

 public void start() { new Thread(new TaskC.start(); new Thread(new TaskD.start(); } private Lock reentrantLock = new ReentrantLock(); private Condition condition = reentrantLock.newCondition(); private class TaskC implements Runnable { @Override public void run() { reentrantLock.lock(); System.out.println("线程 C 拿到了锁,开始工作"); try { System.out.println("线程 C 释放了锁,进入等待状态"); condition.await(); System.out.println("线程 C 收到信号,继续工作"); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("线程 C 释放了锁"); reentrantLock.unlock(); } } } private class TaskD implements Runnable { @Override public void run() { reentrantLock.lock(); System.out.println("线程 D 拿到了锁,开始工作"); try { Thread.sleep; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("-----------------------"); try { System.out.println("线程 D 发信号了,完成工作"); condition.signal(); } finally { System.out.println("线程 D 释放了锁"); reentrantLock.unlock(); } } }/*打印:线程 C 拿到了锁,开始工作线程 C 释放了锁,进入等待状态线程 D 拿到了锁,开始工作-----------------------线程 D 发信号了,完成工作线程 D 释放了锁线程 C 收到信号,继续工作线程 C 释放了锁*/

相比 Object 的 wait/notify,Condition 有许多优点:

  • Condition 可以支持多个等待队列,因为一个 Lock 实例可以绑定多个 Condition

  • Condition 支持等待状态下不响应中断

  • Condition 支持当前线程进入等待状态,直到将来的某个时间

使用 wait/notify:

 public void printNumber() { new Thread(new EvenTask.start(); new Thread(new OddTask.start(); } private int number = 10; private final Object numberLock = new Object(); private class EvenTask implements Runnable { @Override public void run() { synchronized (numberLock) { while (number >= 0 && (number & 1) == 0) { System.out.println("偶数: "   ); numberLock.notify(); try { numberLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } private class OddTask implements Runnable { @Override public void run() { synchronized (numberLock) { while (number >= 0 && (number & 1) == 1) { System.out.println("奇数: "   ); numberLock.notify(); try { numberLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }

使用 Lock/Condition:

 public void printNumber() { new Thread(new EvenTask.start(); new Thread(new OddTask.start(); } private int number = 10; private Condition evenCondition = reentrantLock.newCondition(); private Condition oddCondition = reentrantLock.newCondition(); private class EvenTask implements Runnable { @Override public void run() { reentrantLock.lock(); try { while (number >= 0 && (number & 1) == 0) { System.out.println("偶数: "   ); oddCondition.signal(); evenCondition.await(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { reentrantLock.unlock(); } } } private class OddTask implements Runnable { @Override public void run() { reentrantLock.lock(); try { while (number >= 0 && (number & 1) == 1) { System.out.println("奇数: "   ); evenCondition.signal(); oddCondition.await(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { reentrantLock.unlock(); } } }

运行后打印:

偶数: 10奇数: 9偶数: 8奇数: 7偶数: 6奇数: 5偶数: 4奇数: 3偶数: 2奇数: 1偶数: 0

最后,建议使用 Lock/Condition 代替 Object 的 wait/notify,因为前者是 java.util.concurrent 包下的接口,对于同步更简洁高效,多线程操作优先选用 JUC 包的类。

参考文章:

  • 1010cc时时彩标准版,Java 并发:线程间通信与协作

  在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。

线程间协作的两种方式

  在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。

  今天我们就来探讨一下Java中线程协作的最常见的两种方式:利用Object.wait()、Object.notify()和使用Condition

  以下是本文目录大纲:

  一.wait()、notify()和notifyAll()

  二.Condition

  三.生产者-消费者模型的实现

  若有不正之处请多多谅解,并欢迎批评指正。

  请尊重作者劳动成果,转载请标明原文链接:

  

 

  在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。

二,Condition 类增强类

Condition 类,实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition。

Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
调用Condition的await()和signal()方法,必须在lock.lock()和lock.unlock之间才可以使用

 

  今天我们就来探讨一下Java中线程协作的最常见的两种方式:利用Object.wait()、Object.notify()和使用Condition

一.wait()、notify()和notifyAll()

  wait()、notify()和notifyAll()是Object类中的方法:

/**
 * Wakes up a single thread that is waiting on this object's 
 * monitor. If any threads are waiting on this object, one of them 
 * is chosen to be awakened. The choice is arbitrary and occurs at 
 * the discretion of the implementation. A thread waits on an object's 
 * monitor by calling one of the wait methods
 */
public final native void notify();

/**
 * Wakes up all threads that are waiting on this object's monitor. A 
 * thread waits on an object's monitor by calling one of the 
 * wait methods.
 */
public final native void notifyAll();

/**
 * Causes the current thread to wait until either another thread invokes the 
 * {@link java.lang.Object#notify()} method or the 
 * {@link java.lang.Object#notifyAll()} method for this object, or a 
 * specified amount of time has elapsed. 
 * <p>
 * The current thread must own this object's monitor. 
 */
public final native void wait(long timeout) throws InterruptedException;

   从这三个方法的文字描述可以知道以下几点信息:

  1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。

  2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)

  3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;

  4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

  有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。

  上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

  调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);

  notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。

  同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

  nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。

  这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。

  举个简单的例子:假如有三个线程Thread1、Thread2和Thread3都在等待对象objectA的monitor,此时Thread4拥有对象objectA的monitor,当在Thread4中调用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一个能被唤醒。注意,被唤醒不等于立刻就获取了objectA的monitor。假若在Thread4中调用objectA.notifyAll()方法,则Thread1、Thread2和Thread3三个线程都会被唤醒,至于哪个线程接下来能够获取到objectA的monitor就具体依赖于操作系统的调度了。

  上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。

下面看一个例子就明白了:

public class Test {
    public static Object object = new Object();
    public static void main(String[] args) {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();

        thread1.start();

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.start();
    } 

    static class Thread1 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                }
                System.out.println("线程" Thread.currentThread().getName() "获取到了锁");
            }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("线程" Thread.currentThread().getName() "调用了object.notify()");
            }
            System.out.println("线程" Thread.currentThread().getName() "释放了锁");
        }
    }
}

   无论运行多少次,运行结果必定是:

1010cc时时彩标准版 11010cc时时彩标准版 2

线程Thread-1调用了object.notify()
线程Thread-1释放了锁
线程Thread-0获取到了锁

View Code

  今天我们就来探讨一下Java中线程协作的最常见的两种方式:利用Object.wait()、Object.notify()和使用Condition

本文由1010cc时时彩标准版发布于三分时时彩1010CC,转载请注明出处:Java线程间通讯和搭档的三种艺术,线程间协作的

关键词:

上一篇:提交任务到Spark,提交任务Spark

下一篇:没有了