该案例涉及到的知识点:
- wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
- notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
- notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
- wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
- wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常。
- wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
- 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
- 不同点:
- 1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
- 2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
- 3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
案例一:
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}class Number implements Runnable {
private int number = 1;
private Object obj = new Object();
@Override
public void run() {
while (true) {
//synchronized (this) {此处this就是Number只new了一次可以充当同步锁
synchronized (obj) {//继承Runnable方式,obj不需要加static修饰就可以共享
obj.notify();
if (number <= 100) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
//调用wait()方法的线程进入阻塞状态,并且释放锁
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}}}
}
案例二:
public class CommunicationTest {
public static void main(String[] args) {
Number t1 = new Number();
Number t2 = new Number();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}class Number extends Thread {
private static int number = 1;
private static Object obj = new Object();
@Override
public void run() {
while (true) {
//synchronized (obj){用静态的obj也可以,notify()和wait()需要用obj来调用,如果不加就是默认this.notify
//这里的this是Numbernew了2次 不可以用来调用notify()和wait()
//synchronized(this){错误此处this就是Numbernew了2次,不能当同步锁,同步锁顾名思义是相同的唯一的
synchronized (Number.class) {
Number.class.notify();
if (number <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
//调用wait()方法的线程进入阻塞状态,并且释放锁
Number.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}}}
}
案例三:
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Odd t1 = new Odd(number);
Even t2 = new Even(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}class Number {
private int number = 1;
//非静态的同步方法中锁是当前对象,也就是Number
//静态的同步方法中锁是当前类对象Number.class
public synchronized void printNum() {
while (true) {
try {
if (number <= 100) {
notify();
System.out.println(Thread.currentThread().getName() + ":" + number);
//100
number++;
//101
try {
wait();
//调用wait()方法的线程进入等待阻塞状态,并且释放锁//当number到101时,当前线程将处于一直等待状态,因为另外一个线程不会再进来if判断,会执行break结束线程。
//此时程序中就会存在一个一直等待的线程,程序无法终止,有2中解决方案
//方案1:执行下面代码number == 101,调用notify();
唤醒这个仅剩下的一个线程
if (number == 101) {
notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}} finally {
//方案2:这种方案有点多此一举,多加一个try/finally代码块没必要,建议使用第一种
//if (number == 101) {
//notify();
//}
}
}}}class Odd extends Thread {private Number number;
public Odd(Number number) {
this.number = number;
}@Override
public void run() {number.printNum();
}
}class Even extends Thread {private Number number;
public Even(Number number) {
this.number = number;
}@Override
public void run() {number.printNum();
}
}
案例四:
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
NumberThread t1 = new NumberThread(number);
NumberThread t2 = new NumberThread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}class Number {
private int number = 1;
//非静态的同步方法中锁是当前对象,也就是Number
//静态的同步方法中锁是当前类对象Number.class
public synchronized void printNum() {
while (true) {
try {
if (number <= 100) {
notify();
System.out.println(Thread.currentThread().getName() + ":" + number);
//100
number++;
//101
try {
wait();
//调用wait()方法的线程进入等待阻塞状态,并且释放锁//当number到101时,当前线程将处于一直等待状态,因为另外一个线程不会再进来if判断,会执行break结束线程。
//此时程序中就会存在一个一直等待的线程,程序无法终止,有2中解决方案
//方案1:执行下面代码number == 101,调用notify();
唤醒这个仅剩下的一个线程
//if (number == 101) {
//notify();
//}
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}} finally {
//方案2:这种方案有点多此一举,多加一个try/finally代码块没必要,建议使用第一种
if (number == 101) {
notify();
}
}
}}}class NumberThread extends Thread {private Number number;
public NumberThread(Number number) {
this.number = number;
}@Override
public void run() {number.printNum();
}
}
【使用两个线程打印 1-100。线程1, 线程2 交替分别打印奇数和偶数】方案四其实就是对方案三的一种简化,不需要再多写个类继承Thread。
推荐阅读
- 代码狂魔|实战证明java中的两把锁ReentrantLock与synchronized的系统调用
- 进程通信方式
- 解决方案|大文件拆分方案的java实践
- 多线程编程(1)(共享内存与锁)
- Java|多线程编程(二)——面试题,每个线程只打印一种字符,多个线程协同顺序打印n次字符串(求大神的其他实现方案)
- 一道面试题(多个线程按顺序输出)
- 多线程|java多线程实现奇偶数输出
- 面试题--三个线程循环打印ABC 10次(另类解决方法)
- 用信号量(互斥锁)实现两个线程交替打印