前言
正在成都面试Java后端开发,有老板缺人可以M一下~
问题描述
Java笔试题出现这个问题概率还挺大,手写一下两个线程交替打印一组序列或者按顺序两组序列,本文以后者为例。线程1打印1,2,3,4,5,线程2打印a,b,c,d,e。问题本质就是一个简单的线程同步实现,同步的操作是打印
解决思路
两个线程中run方法(Runnable对象)主体是一个循环,循环内部是同步代码,所以在程序外需要一个共用的🔒对象。可以使用synchronized语句块(1.4提供)结合notify和wait机制实现,但是存在后一个打印线程阻塞的问题(因为这个线程最后一次释放🔒--wait之后没有地方调用notify/notifyAll来通知它再次获取🔒)导致程序阻塞。所以这里的解法使用Lock和Condition(1.5)的实现办法
代码
/**
这里实现的是三个线程同时参与打印三元组的序列,和本题考察点相同
*/
public static void main(String[] args) {
Lock lock = new ReentrantLock(); // 使用ReentrantLock,即一个线程可以重复获得🔒
Condition rotate = lock.newCondition(); // 一个Lock实例可以有多个条件实例,这里只需要通过newCondition来获得一个
final AtomicInteger seq = new AtomicInteger(1); // 协助判断条件,lambda中使用的外部变量必须是final的,普通的Integer也可以,因为这个变量的操作是在同步代码内
new Thread(() -> {
int i = 0;
while (i <= 5) {
lock.lock(); // 加锁
try {
while (!(seq.get() % 3 == 1)) {
rotate.await(); // 如果当前不该它打印,则即使获得🔒,也应该交出去并继续等待。这里相当于等待的是条件而不是🔒
}
System.out.printf("<%d, ", i++);
seq.incrementAndGet(); // 修改条件的内容
rotate.signalAll(); // 通知其它线程获取🔒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 推荐写法,在finally语句块中释放🔒
}
}
}).start();
new Thread(() -> {
int i = 5;
while (i >= 0) {
lock.lock();
try {
while (!(seq.get() % 3 == 2)) {
rotate.await();
}
System.out.printf("%d, ", i--);
seq.incrementAndGet();
rotate.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
new Thread(() -> {
int i = 6;
while (i <= 10) {
lock.lock();
try {
while (!(seq.get() % 3 == 0)) {
rotate.await();
}
System.out.printf("%d>", i--);
seq.incrementAndGet();
rotate.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
}
备注
参考书籍 《Java Core》ed11,竞争条件部分
题目虽然不难,但是这应该是解决经典的同步问题的基本思路和实现,记录一下