使用两个线程打印 1-100。线程1, 线程2 交替分别打印奇数和偶数

该案例涉及到的知识点:

  1. wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
  2. notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
  3. notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
注意点:
  1. wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
  2. wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常。
  3. wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
相关面试题:sleep() 和 wait()的异同?
  • 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
  • 不同点:
  • 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。

    推荐阅读