1.概述
Java所有的类都继承Object对象。Object对象有以下常用方法:
// 克隆当前对象
protected Object clone()
// 判断对象内存空间地址是否相同
boolean equals(Object obj)
// 显示调用对象清理
protected void finalize()
// 获取当前对象的Class对象,可以获取方法区里类的元数据信息
Class<?> getClass()
// 每个对象都有一个hashCode和内存中的地址相对应,Java集合对象常用
int hashCode()
// 唤醒当前waiting的线程
void notify()
// 唤醒所有waiting的线程
void notifyAll()
// 方便定位错误,给每个成员变量加入到toString()
String toString()
// 调用wait(),进入等待状态
void wait()
// 指定当前wait的等待时间
void wait(long timeout)
//它允许更好地控制在放弃之前等待通知的时间量。用毫微秒度量的实际时间量
void wait(long timeout, int nanos)
2.wait(),notify/notifyAll()方法简介
- wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写。
- wait()使当前线程阻塞,前提是必须先获得锁,一般配合synchronized 关键字使用,在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。
没有用synchronized修饰run()方法会抛出异常:
public class JavaWait01 implements Runnable {
@Override
public void run() {
System.out.println("hello wait");
try {
wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("waiting over");
}
public static void main(String[] args) {
JavaWait01 javaWait01 = new JavaWait01();
Thread thread = new Thread(javaWait01);
thread.start();
}
}
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
hello wait
at java.lang.Object.wait(Native Method)
at netty.sd.threadpool.JavaWait01.run(JavaWait01.java:9)
at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 08
添加synchronize修饰 run()方法:
public class JavaWait01 implements Runnable {
@Override
public synchronized void run() {
System.out.println("hello wait");
try {
wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("waiting over");
}
public static void main(String[] args) {
JavaWait01 javaWait01 = new JavaWait01();
Thread thread = new Thread(javaWait01);
thread.start();
}
}
hello wait
waiting over
Process finished with exit code 0
- 由于 wait()、notify/notifyAll() 在synchronized 代码块执行,说明当前线程一定是获取了锁的
当线程执行wait()方法时候,会释放当前的锁,然后释放CPU,进入等待状态。
只有当notify/notifyAll()被执行时候,才会唤醒一个或多个正处于等待状态
的线程,然后继续往下执行,直到执行完synchronized代码块的代码或是中途遇到wait() ,再次释放锁,
也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具
体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程
- wait() 需要被try catch包围,中断也可以使wait等待的线程唤醒
public class JavaWait02 implements Runnable {
@Override
public synchronized void run() {
System.out.println("hello wait");
try {
wait();
} catch (InterruptedException e) {
System.out.println("wait 被中断");
e.printStackTrace();
}
System.out.println("waiting over");
}
public static void main(String[] args) {
JavaWait02 javaWait01 = new JavaWait02();
Thread thread = new Thread(javaWait01);
thread.start();
thread.interrupt();
}
}
hello wait
java.lang.InterruptedException
wait 被中断
at java.lang.Object.wait(Native Method)
waiting over
at java.lang.Object.wait(Object.java:502)
at netty.sd.threadpool.JavaWait02.run(JavaWait02.java:9)
at java.lang.Thread.run(Thread.java:748)
- notify 和wait 的顺序不能错,如果A线程先执行notify方法,B线程在执行wait方法,那么B线程是无法被唤醒的。
- notify 和 notifyAll的区别:
notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。 - 在多线程中要测试某个条件的变化,使用if 还是while:
当wait()被唤醒以后,会接着原来执行的地方接着执行下去。那么如果使用if()语句进行判断的话,只会进行一次条件判断。程序接着执行下去。可能存在这种情况,当前线程虽然被换醒了,依然存在没有达到执行条件的问题。如果使用while()进行条件判断的话,不管wait()是否被唤醒,都会进行条件判断,直到当前wait()被唤醒且while()的条件满足。
3.生产者消费者代码实现
3.1 生产者代码实现:
public class Producer implements Runnable {
private LinkedList<Integer> resources;
/*当前仓库最大容量*/
private int maxSize;
/*需要生产的产品总数*/
private int proSize;
public Producer(LinkedList<Integer> resources, int maxSize, int proSize) {
this.resources = resources;
this.maxSize = maxSize;
this.proSize = proSize;
}
@Override
public void run() {
synchronized (resources) {
for (int i = 0; i < proSize; i++) {
// 当生产的产品总数大于仓库容量时,进行阻塞
while (resources.size() > maxSize) {
System.out.println("当前仓库已满,等待消费...");
try {
// 进入阻塞状态
resources.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("已经生产产品数:" + i + "\t现仓储量总量:" + resources.size());
resources.add(i);
// 唤醒所有消费者
resources.notifyAll();
// 唤醒单个消费者
//resources.notify();
}
}
}
}
3.2 消费者代码实现:
public class Consumer implements Runnable {
private LinkedList<Integer> resources;
public Consumer(LinkedList<Integer> resources) {
this.resources = resources;
}
@Override
public void run() {
String threadName = "thread_name" + Thread.currentThread().getName();
while (true) {
synchronized (resources) {
// 当前仓库没有产品的时候,进行阻塞
while (resources.size() <= 0) {
System.out.println(threadName + " 当前仓库没有产品,请稍等...");
try {
// 进入阻塞状态
resources.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费数据
int size = resources.size();
for (int i = 0; i < size; i++) {
Integer remove = resources.remove();
System.out.println(threadName + " 当前消费产品编号为:" + remove);
}
// 唤醒生产者
resources.notify();
}
}
}
}
3.3 测试用例:
public class ProducerConsumerTest {
public static void main(String[] args) throws InterruptedException {
LinkedList<Integer> integers = new LinkedList<>();
Producer producer = new Producer(integers, 10, 20);
Consumer consumer = new Consumer(integers);
Thread producerThread = new Thread(producer);
Thread consumer1thread = new Thread(consumer, "consumer_1");
Thread consumer2thread = new Thread(consumer, "consumer_2");
consumer2thread.start();
consumer1thread.start();
TimeUnit.SECONDS.sleep(2);
producerThread.start();
}
}
3.4 输出结果:
thread_nameconsumer_1 当前仓库没有产品,请稍等...
thread_nameconsumer_2 当前仓库没有产品,请稍等...
已经生产产品数:0 现仓储量总量:0
已经生产产品数:1 现仓储量总量:1
已经生产产品数:2 现仓储量总量:2
已经生产产品数:3 现仓储量总量:3
已经生产产品数:4 现仓储量总量:4
已经生产产品数:5 现仓储量总量:5
已经生产产品数:6 现仓储量总量:6
已经生产产品数:7 现仓储量总量:7
已经生产产品数:8 现仓储量总量:8
已经生产产品数:9 现仓储量总量:9
已经生产产品数:10 现仓储量总量:10
当前仓库已满,等待消费...
thread_nameconsumer_2 当前消费产品编号为:0
thread_nameconsumer_2 当前消费产品编号为:1
thread_nameconsumer_2 当前消费产品编号为:2
thread_nameconsumer_2 当前消费产品编号为:3
thread_nameconsumer_2 当前消费产品编号为:4
thread_nameconsumer_2 当前消费产品编号为:5
thread_nameconsumer_2 当前消费产品编号为:6
thread_nameconsumer_2 当前消费产品编号为:7
thread_nameconsumer_2 当前消费产品编号为:8
thread_nameconsumer_2 当前消费产品编号为:9
thread_nameconsumer_2 当前消费产品编号为:10
thread_nameconsumer_2 当前仓库没有产品,请稍等...
thread_nameconsumer_1 当前仓库没有产品,请稍等...
已经生产产品数:11 现仓储量总量:0
已经生产产品数:12 现仓储量总量:1
已经生产产品数:13 现仓储量总量:2
已经生产产品数:14 现仓储量总量:3
已经生产产品数:15 现仓储量总量:4
已经生产产品数:16 现仓储量总量:5
已经生产产品数:17 现仓储量总量:6
已经生产产品数:18 现仓储量总量:7
已经生产产品数:19 现仓储量总量:8
thread_nameconsumer_1 当前消费产品编号为:11
thread_nameconsumer_1 当前消费产品编号为:12
thread_nameconsumer_1 当前消费产品编号为:13
thread_nameconsumer_1 当前消费产品编号为:14
thread_nameconsumer_1 当前消费产品编号为:15
thread_nameconsumer_1 当前消费产品编号为:16
thread_nameconsumer_1 当前消费产品编号为:17
thread_nameconsumer_1 当前消费产品编号为:18
thread_nameconsumer_1 当前消费产品编号为:19
thread_nameconsumer_1 当前仓库没有产品,请稍等...
thread_nameconsumer_2 当前仓库没有产品,请稍等...