一、Object自带的方法
1.wait
1.1 wait()方法,在其他线程调用notify和notifyAll方法之前,当前线程等待。
什么意思了,就是在当某一个线程在执行一个任务时,这个任务是一个方法,方法中有一个wait方法,当线程执行到这句时,就会停止执行,当前线程处于阻塞状态,但是会释放对象锁,意思就是会把钥匙交出去,这时候其他线程试图拿到了此对象锁,进行执行该任务,只有当执行到了任务里面的notify和notifyAll方法时,那个处于阻塞状态的线程才会被唤醒。
1.2 wait(long timeout)这个方法呢,和上一个方法类似,但是不同的一点是,就算没有其他线程来调用notify方法,当超过了它传入的时候后,线程也会被唤醒。
2.notify和notifyAll
2.1 notify方法刚才应用上上面讲的wait()方法,调用notify方法后,便会唤醒在执行该任务的等待的单个线程。
2.2 notifyAll方法,与上面不同的便是,notify只会唤醒等待的单个线程,但如果有多个线程都是该任务的等待线程,这时便可以使用notifyAll唤醒所以的等待线程。
二、生产消费模式
1.前言
1.1 为了更好的了解wait和notify方法,我们引入了生产消费模式来加强我们的理解,这两个方法也通常用于这种模式中,实现的场景。比如一个公司有内网。现在我们的服务器是在外网中。在外网的服务器如果有一个用户登录了,需要通知到内网进行知晓。这时我们便可以建一个中间服务器来通过生产消费模式来进行监听,当然java中的EJB,JMS这些消息传递也难实现,但这里主要是讲生产消费模式。外网中有一个用户登录,代表生产了一个信息,中间服务器进行接受,然后把这条消息传递到内网,实现消费过程。这个列子可能比较抽象,其实生产消费模式,也就是你负责炸爆米花,我负责吃
2.uml图
2.1 这里我还是把任务和线程分开来写,就在Thread里面写任务内容更方便,我分开的目的还是要明确一点,线程是线程,任务是任务,线程只是执行任务的工具。
3.代码
3.1 Productor为生产类。用来生产产品,queue为我们传入的仓库对象,仓库是有限的,当仓库的东西满了之后,便停止生产。调用了wait方法,电源就好比是线程,生产机械就是这个类,用来生产东西,当不需要生产时,拔掉电源就好了。
package com.test;
import java.util.Queue;
import java.util.Random;
public class Productor{
private Queue<String> queue;
private int maxthing;
public Productor(Queue<String> queue,int maxthing){
this.queue=queue;
this.maxthing=maxthing;
}
public void pro(){
while (true) {
synchronized(queue){
while (queue.size()== maxthing) {
System.out.println("仓库已满"+
Thread.currentThread().getName()+"进入wait状态");
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//仓库没满,可继续生产
System.out.println(Thread.currentThread().getName()+"生产一个,仓库总量:"+queue.size());
queue.offer("爆米花");
queue.notifyAll();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
3.2 Customer为我们的消费类,当仓库没有物品时候,我们就无法消费,便只有等待仓库有物品时来唤醒我们当前的等待线程来消费。
package com.test;
import java.util.Queue;
import java.util.Random;
public class Customer {
private Queue<String> queue;
public Customer(Queue<String> queue,int maxthing){
this.queue=queue;
}
public void cus(){
while(true){
synchronized(queue){
while (queue.size()== 0) {
System.out.println("仓库已空"+
Thread.currentThread().getName()+"进入wait状态");
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//仓库没空,可消费
System.out.println(Thread.currentThread().getName()+"消费一个,仓库总量:"+queue.size());
queue.poll();
queue.notifyAll();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
3.3 ThreadProduct生产线程,用来执行我们的生产任务。
package com.test;
public class ThreadProduct extends Thread{
private Productor productor;
public ThreadProduct(String name,Productor productor) {
super(name);
this.productor=productor;
}
@Override
public void run() {
productor.pro();
}
}
3.4 ThreadCustom消费线程,用来执行我们的消费任务。
package com.test;
public class ThreadCustom extends Thread{
private Customer customer;
public ThreadCustom(String name,Customer customer) {
super(name);
this.customer=customer;
}
@Override
public void run() {
customer.cus();
}
}
3.5 Test4测试类
package com.test;
import java.util.LinkedList;
import java.util.Queue;
public class Test4 {
private static int MAXTHING=10;
private static Queue<String> queue=new LinkedList<String>();
public static void main(String[] args) {
Productor productor=new Productor(queue, MAXTHING);
Customer customer=new Customer(queue, MAXTHING);
Thread pr1=new ThreadProduct("pr1",productor);
Thread pr2=new ThreadProduct("pr2",productor);
Thread cu1=new ThreadCustom("cu1",customer);
Thread cu2=new ThreadCustom("cu2",customer);
pr1.start();
pr2.start();
cu1.start();
cu2.start();
}
}
4.结果
4.1 运行结果:
pr1生产一个,仓库总量:0
pr1生产一个,仓库总量:1
pr1生产一个,仓库总量:2
pr1生产一个,仓库总量:3
pr1生产一个,仓库总量:4
pr1生产一个,仓库总量:5
pr1生产一个,仓库总量:6
pr1生产一个,仓库总量:7
pr1生产一个,仓库总量:8
pr1生产一个,仓库总量:9
仓库已满pr1进入wait状态
cu2消费一个,仓库总量:10
cu2消费一个,仓库总量:9
cu2消费一个,仓库总量:8
cu2消费一个,仓库总量:7
cu2消费一个,仓库总量:6
cu2消费一个,仓库总量:5
cu2消费一个,仓库总量:4
cu2消费一个,仓库总量:3
4.2 结果分析,不出意外的话,我们的程序会一直运行下去,不停的生产,不停的消费。
5. 总结
5.1 wait和notify都是属于object的方法,所以我们任何类都可以去调用这两个方法。
5.2 额外说一下,sleep方法也可以是线程休眠,但是sleep方法是线程独有了,就好比一种是主动,一种是被动,就好比,你在公司上班,老板是线程,你是任务,老板叫你去哪,你就去哪,调用wait方法,是你自己辞职,不想让老板指挥你了,而调用sleep方法,是老板炒你鱿鱼,他不想指挥你了,虽然最种你都不会被别人指挥,结果相同,但过程不一样。sleep不会释放锁,wait会释放锁。你主动辞职了,证明你有能力,想去更好的地方发展,其他老板也都很愿意要你,你可以去其他公司。但是老板炒你鱿鱼,代表你真的不行,其他公司也不愿收留你。列子虽然举得不恰当,但大概是这意思。其实并不复杂,你多看两遍,多理解下,就很容易区分。当然线程还有其他很多方法,这里就不细讲了,主要在于进步,而不要想什么一天就搞懂。以前我有个室友,期末的时候看了两个小时高数书,他说他会了,结果挂了,人家一学期都学不懂的,你又怎么可能短时间学会,后来我们就经常比喻,你以为你是两个小时学完高数哦。扯多了,大概就是这意思。其实我也是个初入互联网的菜鸟,边学习,边写笔记。不要怕自己讲错了,不讲你永远不知道自己对不对,讲出来了,你好歹知道自己哪里不对,该怎么改。希望各位大神指出错误之处或不足之处,一定改之。