这是一道面试题:
启动两个线程, 一个输出 1,3,5,7…99, 另一个输出 2,4,6,8…100,最后按顺序输出 1,2,3,4,5…100
思路
线程1输出一个数字后,累加计数器,然后唤醒线程2并等待;线程2开始执行,执行的内容与线程1相同。这里因为只有2个线程,所以无需判断奇偶,只需要控制执行顺序就行了。
方式1:使用wait/nofity来实
public class WaitNotifyTest {
private static int count = 1;
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (count <= 100) {
synchronized (lock) {
System.out.println("odd: " + count++);
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(() -> {
while (count <= 100) {
synchronized (lock) {
System.out.println("even: " + count++);
lock.notify();
// 为了让程序正常退出
// 在输出100后,结束线程
if (count == 101) {
return;
}
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
// 为避免顺序出错,先sleep再启动t2
Thread.sleep(1);
t2.start();
// 等待最后执行的线程结束
t2.join();
}
}
上述代码中,因为2个线程是交替执行的,所以它们之间不存在并发的问题,但是为了使用wait/notify提供的功能,必须使用synchronized锁。那有没有更简洁的方案呢?
方式2:使用park/unpark实现
JDK提供的park/unpark用于阻塞、唤醒线程,它是基于permit(许可证)机制,对执行顺序没要求。相对于方式1来说,更加简单直观。
public class LockSupportTest {
private static int count = 1;
static class Threads {
Thread t1;
Thread t2;
void join() throws InterruptedException {
t1.join();
t2.join();
}
}
public static void main(String[] args) throws InterruptedException {
final Threads threads = new Threads();
threads.t1 = new Thread(() -> {
while (count <= 100) {
System.out.println("t1: " + count++);
// 唤醒t2
LockSupport.unpark(threads.t2);
// 阻塞自己
LockSupport.park();
}
});
threads.t2 = new Thread(() -> {
while (count <= 100) {
System.out.println("t2: " + count++);
// 唤醒t1
LockSupport.unpark(threads.t1);
if (count <= 100) {
LockSupport.park();
}
}
});
threads.t1.start();
Thread.sleep(1);
threads.t2.start();
threads.join();
}
}
整体代码思路和wait/notify类似,不再赘述。