使用两个线程打印 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是Number  new了2次 不可以用来调用notify()和wait()
            //synchronized(this){错误  此处this就是Number  new了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();

    }
}

方案四其实就是对方案三的一种简化,不需要再多写个类继承Thread。

你可能感兴趣的:(多线程)