1 线程间交互
线程间的交互,也就是相互通知,进而达到相互控制,java中线程间的交互要用到java.lang.Object的类的三个方法分别是wait,notify(),notifyAll,三个方法的调用必须在同步环境内调用,也就是线程获取了对象的锁后才能调用。
如果线程A持有线程B的对象的锁,多线程环境下只有当线程A获取了线程B的锁后(同步环境下)时,线程A才能调用B的wait,notify(),notifyAll方法。
wait()还有另外两个重载方法:
void wait(long timeout):等待直到其他线程调用此对象的 notify() 或 notifyAll() ,或者超过timeout时间量。
void wait(long timeout, int nanos):等待直到其他线程调用此对象的 notify() 或 notifyAll() ,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
wait函数会释放锁。
/**
wait函数会立即释放对象锁,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,才会被重新激活。
* Causes the current thread to wait until another thread invokes the
当前线程必须拥有交互对象的monitor(什么是monitor呢,会在后面讲,
这里大家把monitor理解为一个对象,每个object只和一个monitor相关联,一个线程获取monitor之后,除非他主动释放
其他线程无法获取monitor)。
如果当前线程没有获取monitor,它将一直等待,直到其他线程调用该对象的notify,notifyAll唤醒当前线程。
既然调用notify和notifyall能够唤醒线程,所以必定在该对象上存在一个线程链,
当监听到调用了notify时,可以通知该对象上的线程链中的线程。
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
* 由于必须要获取monitor,所以wait调用必须在同步代码块内,且当前线程获取了对应object的monitor,如下所示。
* * This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
* As in the one argument version, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
public final void wait() throws InterruptedException {
wait(0);
}
void notify():唤醒在此对象监视器上等待的单个线程。
void notifyAll():唤醒在此对象监视器上等待的所有线程。
notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。
/**
调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
notify和notifyAll的调用也必须在获取对象monitor的状态下,函数会唤醒该对象线程链中的一个线程。
* 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 {@code wait} methods.
* <p>
* The awakened thread will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened thread will
* compete in the usual manner with any other threads that might be
* actively competing to synchronize on this object; for example, the
* awakened thread enjoys no reliable privilege or disadvantage in being
* the next thread to lock this object.
* <p>
* This method should only be called by a thread that is the owner
* of this object's monitor. A thread becomes the owner of the
* object's monitor in one of three ways:
* <ul>
* <li>By executing a synchronized instance method of that object.
* <li>By executing the body of a {@code synchronized} statement
* that synchronizes on the object.
* <li>For objects of type {@code Class,} by executing a
* synchronized static method of that class.
* </ul>
* <p>
*/
public final native void notify();
/**
多个线程可以等待一个对象锁,当需要唤醒所有线程时,可以调用notifyAll。
调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
notifyAll函数会唤醒所有在等待object monitor的线程,最终能够获取monitor的只有一个线程。
* 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
* {@code wait} methods.
* <p>
* The awakened threads will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened threads
* will compete in the usual manner with any other threads that might
* be actively competing to synchronize on this object; for example,
* the awakened threads enjoy no reliable privilege or disadvantage in
* being the next thread to lock this object.
* <p>
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of this object's monitor.
* @see java.lang.Object#notify()
* @see java.lang.Object#wait()
*/
public final native void notifyAll();
注意:
在对象上调用wait()方法时,执行该代码的线程立即放弃它在对象上的锁。然而调用notify()时,并不会立即释放其锁。会在完成同步代码块后,释放锁,所以调用notify并不会导致立即释放锁。
//下面的代码每次都会先输出B的值
public class Demo3 {
static Object object = new Object();
public static void main(String[] args) {
new Thread(new ThreadA()).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new ThreadB()).start();
}
static class ThreadA implements Runnable{
@Override
public void run() {
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadA");
}
}
}
static class ThreadB implements Runnable{
@Override
public void run() {
synchronized (object) {
try {
object.notify();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("ThreadB");
}
}
}
}
如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)
调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
2 生产者消费者
生产者消费者模型最常用的线程交互模型,当仓库物品为空时无法消费,当仓库满时无法生产。
package com.sync.demo;
import java.util.ArrayList;
import java.util.Random;
public class Demo4 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Thread con1 = new Thread(new Consumer(list));
Thread con2 = new Thread(new Consumer(list));
Thread con3 = new Thread(new Consumer(list));
Thread pro = new Thread(new Productor(list));
con1.start();
con2.start();
con3.start();
pro.start();
}
static class Consumer implements Runnable{
private ArrayList<String> list;
public Consumer(ArrayList<String> list) {
this.list = list;
}
private void consumer() {
synchronized (list) {
while (list.size() < 1) {
System.out.println("=======仓库已空,无法消费,请等待======"+list.size());
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(70);
list.remove(list.size() -1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=======消费一个======剩余======"+Thread.currentThread().getName()+" "+list.size());
list.notify();
}
}
@Override
public void run() {
while (true) {
consumer();
}
}
}
static class Productor implements Runnable{
private ArrayList<String> list;
public Productor(ArrayList<String> list) {
this.list = list;
}
private void productor() {
synchronized (list) {
while (list.size() == 77) {
System.out.println("=======仓库已满,停止生产======"+Thread.currentThread().getName()+" "+list.size());
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(50);
list.add((new Random().nextInt(100))+"pro");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=======生产一个======剩余======"+list.size());
list.notify();
}
}
@Override
public void run() {
while (true) {
productor();
}
}
}
}
结果:
=======生产一个======剩余======59
=======生产一个======剩余======60
=======生产一个======剩余======61
=======生产一个======剩余======62
=======生产一个======剩余======63
=======生产一个======剩余======64
=======生产一个======剩余======65
=======生产一个======剩余======66
=======生产一个======剩余======67
=======生产一个======剩余======68
=======生产一个======剩余======69
=======生产一个======剩余======70
=======生产一个======剩余======71
=======生产一个======剩余======72
=======生产一个======剩余======73
=======生产一个======剩余======74
=======生产一个======剩余======75
=======生产一个======剩余======76
=======生产一个======剩余======77
=======仓库已满,停止生产======Thread-3 77
=======消费一个======剩余======Thread-0 76
=======消费一个======剩余======Thread-0 75
=======消费一个======剩余======Thread-0 74
=======消费一个======剩余======Thread-0 73
=======消费一个======剩余======Thread-0 72
=======消费一个======剩余======Thread-0 71
=======消费一个======剩余======Thread-0 70
=======消费一个======剩余======Thread-0 69
=======消费一个======剩余======Thread-0 68
=======消费一个======剩余======Thread-0 67
=======消费一个======剩余======Thread-0 66
=======消费一个======剩余======Thread-0 65
=======消费一个======剩余======Thread-0 64
=======消费一个======剩余======Thread-0 63
=======消费一个======剩余======Thread-0 62
=======消费一个======剩余======Thread-0 61
=======消费一个======剩余======Thread-0 60
=======消费一个======剩余======Thread-0 59
=======消费一个======剩余======Thread-0 58
=======消费一个======剩余======Thread-0 57
=======消费一个======剩余======Thread-0 56
=======消费一个======剩余======Thread-0 55
=======消费一个======剩余======Thread-0 54
=======消费一个======剩余======Thread-0 53
=======消费一个======剩余======Thread-0 52
=======消费一个======剩余======Thread-0 51
=======消费一个======剩余======Thread-0 50
=======消费一个======剩余======Thread-0 49
=======消费一个======剩余======Thread-0 48
=======消费一个======剩余======Thread-1 47
=======消费一个======剩余======Thread-1 46
=======消费一个======剩余======Thread-1 45
=======消费一个======剩余======Thread-1 44
=======消费一个======剩余======Thread-1 43
=======消费一个======剩余======Thread-1 42
=======消费一个======剩余======Thread-1 41