题目:使用两个线程交替打印1-10

依照题意:线程0打印1,线程1打印2,接着再次这样循环,一直到输出10为止。

我首先想到的是线程间的通信,首先让线程0执行,输出1,然后notify唤醒线程1,之后线程0立马wait释放锁。线程1拿到锁之后,输出2,同样,notify唤醒线程0,接着自己立马wait释放锁。

先写个示例代码:

package com.yang.testThreadPrint;

public class Main {

    //输出的最大数字
    private static final int MAX_NUM = 2;
    //自增变量
    private static volatile int i = 1;
    //临界资源
    private static final Object object = new Object();

    public static void main(String[] args) {

        Runnable r = new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    while (i <= MAX_NUM) {
                        System.out.println(Thread.currentThread().getName() + ":" + i++);
                        try {
                            object.notify();
                            object.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //防止有子线程被阻塞未被唤醒,导致主线程不退出
                    object.notify();
                }
            }
        };

        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();
    }
}

输出结果:


顺便补充一些wait与notify的基础

wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行, 只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还 在别人手里,别人还没释放。如果notify/notifyAll方法后面的代码还有很多,需要这些代码执行完后才会释放锁),调用wait方法的一个或多个线程就会解除wait状态,重新参与竞争对象锁,程序如果可以再次得到锁,就可以继续向下运行。

  • wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
  • wait()必须在同步块之内,且需要捕捉异常
  • 当前线程必须拥有此对象的锁,才能调用某个对象的wait()方法能让当前线程阻塞,(这种阻塞是通过提前释放synchronized锁,重新去请求锁导致的阻塞,这种请求必须有其他线程通过notify()或者notifyAll()唤醒重新竞争获得锁)
  • 调用某个对象的notify()方法能够唤醒一个正在等待这个对象锁的线程,如果有多个线程都在等待这个对象锁,则只能唤醒其中一个线程
  • 调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程,唤醒的线程获得锁的概率是随机的,取决于cpu调度

关于wait()与sleep()的区别,可以参考我的另外一篇文章sleep与wait的区别


上面是使用synchronized+wait+notify,下面使用ReentranLock+condition,本质上都是线程间通信

package com.yang.testThreadPrint;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MainByLock {

    //输出的最大数字
    private static final int MAX_NUM = 10;
    //自增变量
    private static volatile int i = 1;
    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();


    public static void main(String[] args) {

        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (i <= MAX_NUM) {
                        System.out.println(Thread.currentThread().getName() + ":" + i++);
                        condition.signal();
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        condition.signal();
                    }
                } finally {
                    lock.unlock();
                }
            }
        };

        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();
    }
}