线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作。
想像一个场景,A线程做ArrayList的add操作,B线程等待ArrayList的大小到了100000就给出反应,怎么处理?一个办法就是,B线程轮询检查ArrayList的大小,这样两个线程之间就有了通信。
package com.mook.wait;
import java.util.ArrayList;
public class Thread0 extends Thread {
private volatile ArrayList<Integer> list = new ArrayList<>();
public ArrayList<Integer> getList() {
return list;
}
public void setList(ArrayList<Integer> list) {
this.list = list;
}
@Override
public void run() {
while (true) {
list.add(0);
if (list.size() == 100000) {
Thread.yield();
System.out.println(Thread.currentThread().getName() + ": list size = " + list.size());
break;
}
}
}
}
package com.mook.wait;
import java.util.ArrayList;
public class Thread1 extends Thread {
private volatile ArrayList<Integer> list;
public Thread1(ArrayList<Integer> list) {
this.list = list;
}
@Override
public void run() {
while (true) {
if (list.size() == 100000) {
System.out.println(Thread.currentThread().getName() + ": " + list.size());
break;
}
}
}
}
package com.mook.wait;
public class Test {
public static void main(String[] args) {
Thread0 thread0 = new Thread0();
Thread1 thread1 = new Thread1(thread0.getList());
thread0.start();
thread1.start();
}
}
运行结果:两条语句的顺序不一定
Thread-0: list size = 100000
Thread-1: 100000
这样可以实现我们的需求,但是也带来了问题:CPU把资源浪费了B线程的轮询操作上,因为while操作并不释放CPU资源,导致了CPU会一直在这个线程中做判断操作。如果可以把这些轮询的时间释放出来,给别的线程用,就好了。
wait/notify
public final native void wait(long timeout) throws InterruptedException;
public final native void notify();
在Object对象中有三个方法wait()、notify()、notifyAll(),既然是Object中的方法,那每个对象自然都是有的。
1、wait()
wait()的作用是使当前执行代码的线程进行等待,将当前线程置入”预执行队列”中,并且wait()所在的代码处停止执行,直到接到通知或被中断。在调用wait()之前,线程必须获得该对象的锁,因此只能在同步方法/同步代码块中调用wait()方法。
2、notify()
notify()的作用是,如果有多个线程等待,那么线程规划器随机挑选出一个wait的线程,对其发出通知notify(),并使它等待获取该对象的对象锁。注意”等待获取该对象的对象锁”,这意味着,即使收到了通知,wait的线程也不会马上获取对象锁,必须等待notify()方法的线程释放锁才可以。和wait()一样,notify()也要在同步方法/同步代码块中调用。
总结起来就是,wait()使线程停止运行,notify()使停止运行的线程继续运行。
package com.mook.wait.test1;
public class MyThread30_0 extends Thread
{
private Object lock;
public MyThread30_0(Object lock)
{
this.lock = lock;
}
public void run()
{
try
{
synchronized (lock)
{
System.out.println("开始------wait time = " + System.currentTimeMillis());
lock.wait();
System.out.println("结束------wait time = " + System.currentTimeMillis());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
package com.mook.wait.test1;
public class MyThread30_1 extends Thread
{
private Object lock;
public MyThread30_1(Object lock)
{
this.lock = lock;
}
public void run()
{
synchronized (lock)
{
System.out.println("开始------notify time = " + System.currentTimeMillis());
lock.notify();
System.out.println("结束------notify time = " + System.currentTimeMillis());
}
}
}
package com.mook.wait.test1;
public class Test {
public static void main(String[] args) throws Exception
{
Object lock = new Object();
MyThread30_0 mt0 = new MyThread30_0(lock);
mt0.start();
Thread.sleep(3000);
MyThread30_1 mt1 = new MyThread30_1(lock);
mt1.start();
}
}
开始------wait time = 1498787973593
开始------notify time = 1498787976596
结束------notify time = 1498787976596
结束------wait time = 1498787976596
- wait()方法可以使调用该线程的方法释放共享资源的锁,然后从运行状态退出,进入等待队列,直到再次被唤醒。
- notify()方法可以随机唤醒等待队列中等待同一共享资源的一个线程,并使得该线程退出等待状态,进入可运行状态。
- notifyAll()方法可以使所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入可运行状态。
- 最后,如果wait()方法和notify()/notifyAll()方法不在同步方法/同步代码块中被调用,那么虚拟机会抛出java.lang.IllegalMonitorStateException,注意一下。
- wait()释放资源锁,然后进入等待队列,直到再次被唤醒;notify()不释放锁,只是去唤醒等待队列中等待同一共享资源的一个线程;sleep不释放资源锁。
interrupt()打断wait()
之前有说过,interrupt()方法的作用不是中断线程,而是在线程阻塞的时候给线程一个中断标识,表示该线程中断。wait()就是”阻塞的一种场景”。
public class ThreadDomain33
{
public void testMethod(Object lock)
{
try
{
synchronized (lock)
{
System.out.println("Begin wait()");
lock.wait();
System.out.println("End wait()");
}
}
catch (InterruptedException e)
{
System.out.println("wait()被interrupt()打断了!");
e.printStackTrace();
}
}
}
public class MyThread33 extends Thread
{
private Object lock;
public MyThread33(Object lock)
{
this.lock = lock;
}
public void run()
{
ThreadDomain33 td = new ThreadDomain33();
td.testMethod(lock);
}
}
public static void main(String[] args) throws Exception
{
Object lock = new Object();
MyThread33 mt = new MyThread33(lock);
mt.start();
Thread.sleep(5000);
mt.interrupt();
}
Begin wait()
wait()被interrupt()打断了!
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at com.xrq.example.e33.ThreadDomain33.testMethod(ThreadDomain33.java:12)
at com.xrq.example.e33.MyThread33.run(MyThread33.java:15)
notifyAll()唤醒所有线程
利用Object对象的notifyAll()方法可以唤醒处于同一监视器下的所有处于wait的线程。
public class ThreadDomain34
{
public void testMethod(Object lock)
{
try
{
synchronized (lock)
{
System.out.println("Begin wait(), ThreadName = " + Thread.currentThread().getName());
lock.wait();
System.out.println("End wait(), ThreadName = " + Thread.currentThread().getName());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
写两个线程,一个调用testMethod(Object lock)的线程,一个notifyAll()线程:
public class MyThread34_0 extends Thread
{
private Object lock;
public MyThread34_0(Object lock)
{
this.lock = lock;
}
public void run()
{
ThreadDomain34 td = new ThreadDomain34();
td.testMethod(lock);
}
}
public class MyThread34_1 extends Thread
{
private Object lock;
public MyThread34_1(Object lock)
{
this.lock = lock;
}
public void run()
{
synchronized (lock)
{
lock.notifyAll();
}
}
}
main函数开三个wait线程,用一个notifyAll的线程去唤醒:
public static void main(String[] args) throws Exception
{
Object lock = new Object();
MyThread34_0 mt0 = new MyThread34_0(lock);
MyThread34_0 mt1 = new MyThread34_0(lock);
MyThread34_0 mt2 = new MyThread34_0(lock);
mt0.start();
mt1.start();
mt2.start();
Thread.sleep(1000);
MyThread34_1 mt3 = new MyThread34_1(lock);
mt3.start();
}
Begin wait(), ThreadName = Thread-0
Begin wait(), ThreadName = Thread-2
Begin wait(), ThreadName = Thread-1
End wait(), ThreadName = Thread-1
End wait(), ThreadName = Thread-2
End wait(), ThreadName = Thread-0