题目:使用两个线程交替打印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();
}
}