线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能形成一个有效的整体,线程间的通讯就是称为一个整体的必用方案,
等待/通知机制的实现
方法wait()的作用是是当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程置入”预执行队列”中,并且在wait()所处的代码停止执行,知道接到通知或被中断。在调用wait()之前,线程必须获得该对象的对象级别锁。即只能在同步方法或者同步代码块中调用wait()方法。在执行wait()方法后,释放该锁。如果调用wait()方法没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类。不需要执行try catch语句。
方法notify()同样要在同步的方法或者代码块中执行,必须持有对象级别的锁,如果没有拿到锁则会抛出IllegalMonitorStateException。该方法是用来通知那些可能等待该对象的对象所的其他线程。
在执行notify()方法后,并不会马上释放该锁,要等到执行notiy()方法的线程将程序执行完毕。也就是退出synchronized代码块后,才会释放锁。
合理的情况下wait()和notify()必须成对出来,因为当一个wait线程运行完毕后,它会释放掉该对象锁,如果该对象没有使用notify()方法会导致其余处在wait()状态的线程将无法获取该锁。
errorCode
public class Demo
{
public static void main(String[] args)
{
try
{
String str = "aaaa";
str.wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
这里写代码片
程序将会抛出IllegalMonitorStateException异常,因为没有获取到锁
关键字synchronized可以将任何一个Object对象作为同步对象来看待,而Java为每个Object都实现了notify()方法和wait()方法。他们必须在synchronized同步代码块中执行,通过调用wait()操作,而处于临界区内的线程进入等待状态,同时释放该对象的锁,而notify操作可以唤醒一个因调用wait操作而处于阻塞状态中的线程进入就绪状态。被重新唤醒的线程会试图重新获得临界区的控制权,也就是锁,并继续执行wait()方法后的代码。如果notify方法调用时候,没有线程处于阻塞状态就会忽视该代码
wait()方法可以使一个线程释放掉共享资源锁,然后从运行状态中退出。进入到等待对待。知道再次被唤醒。
notify()方法可以随机唤醒等待队列中的等待统一共享资源的“一个”线程。并是该线程退出等待队列。进入可运行状态。也就是notify()方法仅通知“一个”线程。
notifyAll()方法可以使所有正在等待队列中等待同一个共享资源的“全部”,线程从等待状态退出。进入可运行状态。此时优先级最高的先执行。哪个线程先执行,取决于JVM虚拟机的实现。
分析线程的状态:
创建一个新的线程对象后,调用start()方法后,线程处在一个runnable,runnable是一个可运行的状态,当线程获取CPU时间片的时候,线程处在running状态。
running状态和runnable状态可以互相切换。当CPU时间片执行完毕后,线程从running回到runnable状态。
线程进入runnable状态有五种情况:
- 调用sleep方法,经过指定的时间后
- 线程调用的阻塞IO已经返回,阻塞方法执行完毕
- 线程成功地获取试图同步的监视器
- 线程正在等待,收到通知
- 处于挂起状态的线程,调用resume方法
Block是阻塞状态,Block状态结束后,进入到runnable状态。
- 调用sleep方法,主动放弃占用的处理资源
- 线程调用了阻塞式的IO方法,该方法返回前,线程处于阻塞状态
- 试图获得到一个同步监视器,但是监视器被其他线程所占有
- 等待通知状态
- 调用了suspend
4、run()方法运行结束后进入销毁,整个线程执行完毕
每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪对象存储了将要获得锁的线程,阻塞队列存储了被阻塞状态的线程,一个线程被唤醒之后进入就绪队列。等待CPU的调度,反之,一个线程被wait后,进入阻塞状态。
wait()方法调用会立即释放锁,notify()方法被调用锁不会立即释放。notify()方法所在的同步synchronized代码块后才释放该锁。
有三种情况会释放掉所持有的锁对象
- 运行完synchronized代码块,和方法会释放掉该锁
- 抛出异常会释放掉该锁
- 执行到wait()方法会释放掉锁
当线程处在wait()方法时,调用interrupt会抛出InterruptedException异常
我们在调用notify()一次只会随机通知一个线程进行唤醒,而notifyAll()会唤醒全部的线程。
wait(long millions)等待毫秒数,当线程在等待的时间内如果遇到notify(),线程将会唤醒,或者等待时间到了,线程也会被唤醒。
来看一个经典的例子生产者和消费者,其中运用到的是等待/通知,进行多线程通讯
package com.tony.ameng;
import java.util.ArrayList;
import java.util.List;
public class MyStack
{
private List list = new ArrayList<>();
synchronized public void push()
{
try
{
while (list.size() >= 50)
{
System.out.println("当仓库中的商品多余50件的时候停止生产!!");
this.wait();
}
list.add("anyString = " + Math.random());
this.notifyAll();//通知消费者线程进行消费
System.out.println("push = " + list.size());
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
synchronized public String pop()
{
String returnString = "";
try
{
while(list.size() == 0)
{
System.out.println("pop操作中的:" +
Thread.currentThread().getName() + "线程处在wait状态");
this.wait();
}
returnString = list.get(0).toString();
list.remove(0);
this.notifyAll();
System.out.println("pop = " + list.size());
} catch (InterruptedException e)
{
e.printStackTrace();
}
return returnString;
}
}
仓库,有两个方法一个生产,一个是消费
package com.tony.ameng;
public class Consumer
{
private MyStack mStack;
public Consumer(MyStack mStack)
{
this.mStack = mStack;
}
public void popService()
{
System.out.println("pop=" + mStack.pop());
}
}
消费者
package com.tony.ameng;
public class CThread extends Thread
{
private Consumer mConsumer;
public CThread(Consumer mConsumer)
{
this.mConsumer = mConsumer;
}
@Override
public void run()
{
while(true)
{
mConsumer.popService();
}
}
}
消费者线程
package com.tony.ameng;
public class Producer
{
private MyStack mStack;
public Producer(MyStack myStack)
{
this.mStack = myStack;
}
public void pushService()
{
mStack.push();
}
}
消费者
package com.tony.ameng;
public class PThread extends Thread
{
private Producer mProducer;
public PThread(Producer mProducer)
{
super();
this.mProducer = mProducer;
}
@Override
public void run()
{
while(true)
{
mProducer.pushService();
}
}
}
生产者线程
package com.tony.ameng;
public class Run
{
public static void main(String[] args)
{
MyStack mStack = new MyStack();
Producer mProducer = new Producer(mStack);
Producer mProducer2 = new Producer(mStack);
Producer mProducer3 = new Producer(mStack);
Producer mProducer4 = new Producer(mStack);
Consumer mConsumer = new Consumer(mStack);
Consumer mConsumer2 = new Consumer(mStack);
Consumer mConsumer3 = new Consumer(mStack);
Consumer mConsumer4 = new Consumer(mStack);
PThread pThread = new PThread(mProducer);
PThread pThread2 = new PThread(mProducer2);
PThread pThread3 = new PThread(mProducer3);
PThread pThread4 = new PThread(mProducer4);
CThread cThread = new CThread(mConsumer);
CThread cThread2 = new CThread(mConsumer2);
CThread cThread3 = new CThread(mConsumer3);
CThread cThread4 = new CThread(mConsumer4);
pThread.start();
pThread2.start();
pThread3.start();
pThread4.start();
cThread.start();
cThread2.start();
cThread3.start();
cThread4.start();
}
}
main函数,多个消费者与生产者的模型