在使用wait/notify方法前我们需要对这两个方法进行简单的介绍:
使用wait/notify方法前提是拥有相同锁的线程才可以实现此机制。
1)wait方法是object的方法,他的作用是使当前执行wait方法的线程等待,在wait方法后面的代码暂停执行,并且释放锁,直到接到通知或被中断为止。在调用wait方法前,线程必须获得该对象的对象级别的锁,即只能在同步方法或同步代码块中调用wait方法。如果调用wait方法时没有适当的锁会抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此不需要try catch捕获异常。
2)notify方法也是要在同步方法或同步代码块中调用,即在调用方法前线程必须获得锁,如果调用notify时没有持有适当的锁,则会抛出IllegalMonitorStateException。该方法的作用时用来通知那些在等待该锁的其他线程,如果有多个线程等待,则按照执行wait方法的顺序对处于wait状态的线程发出一次通知(notify),并使该线程重新获得锁,不过当前线程并不会立即释放锁,呈wait状态的线程也并不会马上获得该对象的锁,而是要等到执行notify方法的线程将程序执行完,也就是退出synchronized同步区域后,该线程才会释放锁,呈wait状态的线程才可以获得锁。
产品类:
public class Goods {
private String brand;//产品品牌
private String name;//产品名称
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Goods{" +
"brand='" + brand + '\'' +
", name='" + name + '\'' +
'}';
}
}
业务类:
public class Service {
private Goods goods;
private boolean flag=false;
public Service(Goods goods) {
this.goods = goods;
}
public synchronized void setGoods(){
if(flag) {
//有商品生产者等待
try {
super.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
goods.setBrand("康师傅");
Thread.sleep(500);
goods.setName("红烧牛肉面");
System.out.println("生产者生产了"+goods.getBrand()+goods.getName());
flag=true;//生产完产品
super.notify();//通知消费者来取
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void getGoods(){
if(!flag) {
//没有商品消费者等待
try {
super.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(500);
System.out.println("消费者取走了"+goods.getBrand()+goods.getName());
flag=false;//取走产品
super.notify();//通知生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
生产者:
public class Producter implements Runnable{
//生产者
private Service service;
public Producter(Service service) {
this.service = service;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
service.setGoods();
}
}
}
消费者:
public class Consumer implements Runnable{
//生产者
private Service service;
public Consumer(Service service) {
this.service = service;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
service.getGoods();
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Goods goods = new Goods();//产品
Service service = new Service(goods);
Producter producter = new Producter(service);//生产者
Consumer consumer = new Consumer(service);//消费者
new Thread(producter).start();
new Thread(consumer).start();
}
}
结果:
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
public class Test {
public static void main(String[] args) {
Goods goods = new Goods();//产品
com.Service service = new Service(goods);
Thread product1= new Thread(new Producter(service));
product1.setName("生产者1");
product1.start();
Thread product2 = new Thread(new Producter(service));
product2.setName("生产者2");
product2.start();
Thread consumer1 = new Thread(new Consumer(service));
consumer1.setName("消费者1");
consumer1.start();
Thread consumer2 = new Thread(new Consumer(service));
consumer2.setName("消费者2");
consumer2.start();
}
}
结果:
生产者1生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面**
生产者2生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者2生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者2生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者2生产者生产了康师傅红烧牛肉面
**消费者1消费者取走了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者2生产者生产了康师傅红烧牛肉面
**消费者2消费者取走了康师傅红烧牛肉面
生产者1生产者生产了康师傅红烧牛肉面
从运行结果来看分析可知有的线程呈现了"假死现象"即一直在waiting状态,不执行任务了。虽然使用了wait/notify通信但是不能保证notify唤醒的是同类,即消费者唤醒消费者,生产者唤醒生产者。
将业务类的notify方法全部换成notifyall方法即可。
public class Servicer {
private Goods goods;
private boolean flag=false;
public Servicer(Goods goods) {
this.goods = goods;
}
public synchronized void setGoods(){
if(flag) {
//有商品生产者等待
try {
super.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
goods.setBrand("康师傅");
Thread.sleep(500);
goods.setName("红烧牛肉面");
System.out.println(Thread.currentThread().getName()+"生产了"+goods.getBrand()+goods.getName());
flag=true;//生产完产品
super.notifyAll();//通知消费者来取
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void getGoods(){
if(!flag) {
//没有商品消费者等待
try {
super.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"取走了"+goods.getBrand()+goods.getName());
flag=false;//取走产品
super.notifyAll();//通知生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public class Tests {
public static void main(String[] args) {
Goods goods = new Goods();//产品
Servicer service = new Servicer(goods);
Thread product1= new Thread(new Producter(service));
product1.setName("生产者1");
product1.start();
Thread product2 = new Thread(new Producter(service));
product2.setName("生产者2");
product2.start();
Thread consumer1 = new Thread(new Consumer(service));
consumer1.setName("消费者1");
consumer1.start();
Thread consumer2 = new Thread(new Consumer(service));
consumer2.setName("消费者2");
consumer2.start();
}
}
结果:
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
关键字synchronized与wait(),notify(),notifyall()方法相结和可以实现wait/notify模式,ReentrantLock类也可以实现同样的功能,但是需要借助Condition对象,condition类是JDK5的技术,具有更好的灵活性,可以实现多路通知功能,也就是在一个Lock对象中可以创建多个condition实例,线程对象在指定的condition中可以有选择性的进行线程通知,在调度上更加灵活。
在使用await/signal机制解决生产者消费者问题前我们需要先了解一下ReentrantLock类的作用,该类是JDK1.5新增的类,能够达到和synchronized关键字同样的效果,并且在功能上更加强大,如具有嗅锁探定(可以通过tryLock方法进行嗅探拿锁,如果当前线程需要的锁被其他线程持有则返回false),多路分支通知(也就是在一个Lock对象中可以创建多个condition实例,线程对象在指定的condition中可以有选择性的进行线程通知,在调度上更加灵活),公平非公平锁(公平锁采用先到先得的策略,每次锁之前会检查队列里有没有排队等待的线程,如果有就将当前线程加到队列中,如果没有就会获得锁。而非公平锁是可以插队的可以后来但是能先得到锁)。
下面我们用await/signal机制来解决生产者消费者问题:
业务类:
public class Service {
private Goods goods;
private boolean flag=false;
private Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
public Service(Goods goods) {
this.goods = goods;
}
public void setGoods(){
lock.lock();
if(flag) {
//有商品生产者等待
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
goods.setBrand("康师傅");
Thread.sleep(500);
goods.setName("红烧牛肉面");
System.out.println("生产者生产了"+goods.getBrand()+goods.getName());
flag=true;//生产完产品
condition.signal();//通知消费者来取
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void getGoods(){
lock.lock();
if(!flag) {
//没有商品消费者等待
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(500);
System.out.println("消费者取走了"+goods.getBrand()+goods.getName());
flag=false;//取走产品
condition.signal();//通知生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Goods goods = new Goods();//产品
Service service = new Service(goods);
Producter producter = new Producter(service);//生产者
Consumer consumer = new Consumer(service);//消费者
new Thread(producter).start();
new Thread(consumer).start();
}
}
结果:
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
生产者生产了康师傅红烧牛肉面
消费者取走了康师傅红烧牛肉面
业务类:
我们同样也可以将上面signal方法改成signalAll方法来解决多个线程“假死问题”
public class Service {
private Goods goods;
private boolean flag=false;
private Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
public Service(Goods goods) {
this.goods = goods;
}
public void setGoods(){
lock.lock();
if(flag) {
//有商品生产者等待
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
goods.setBrand("康师傅");
Thread.sleep(500);
goods.setName("红烧牛肉面");
System.out.println(Thread.currentThread().getName()+"生产了"+goods.getBrand()+goods.getName());
flag=true;//生产完产品
condition.signalAll();//通知消费者来取
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void getGoods(){
lock.lock();
if(!flag) {
//没有商品消费者等待
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"取走了"+goods.getBrand()+goods.getName());
flag=false;//取走产品
condition.signalAll();//通知生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Goods goods = new Goods();//产品
Service service = new Service(goods);
Thread product1= new Thread(new Producter(service));
product1.setName("生产者1");
product1.start();
Thread product2 = new Thread(new Producter(service));
product2.setName("生产者2");
product2.start();
Thread consumer1 = new Thread(new Consumer(service));
consumer1.setName("消费者1");
consumer1.start();
Thread consumer2 = new Thread(new Consumer(service));
consumer2.setName("消费者2");
consumer2.start();
}
}
结果:
生产者2生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者2生产了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者2取走了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面
生产者1生产了康师傅红烧牛肉面
消费者1取走了康师傅红烧牛肉面