面试常见题。问题描述:两个线程分别输出数字和字母,但是交替输出,最终输出结果为1a2b3c……;涉及到多线程锁的知识。
解决方法一:
使用wait和notify,即先定义一个锁,只有在线程持有锁的时候才可以打印,一个线程打印完成本次任务之后,唤醒另外一个线程执行打印,如此交替执行。
public static void main(String[] args) {
Object o = new Object(); // 锁对象,持有此对象的线程才可以执行临界区代码
char[] chars1 = "12345".toCharArray();
char[] chars2 = "ABCDE".toCharArray();
new Thread(()->{
synchronized (o) {
for (int i = 0; i < chars1.length; i++) {
System.out.print(chars1[i]);
o.notify();// 唤醒其他线程
try {
o.wait();// 阻塞自已,注意此处顺序不可以交换,即不可以先wait再notify,原因简单,因为如果wait之后会阻塞自已,后面的程序就无法执行,则无法唤醒。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify(); //必须,避免出现程序无法线束的情况
}
},"t1").start();
new Thread(()->{
synchronized (o) {
for (int i = 0; i < chars2.length; i++) {
System.out.print(chars2[i]);
o.notify();
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
},"t2").start();
}
解决方法二:
使用ReentrantLock和Condition,这种方式的好处是可以实现更多线程的交替输出。可以将condition理解为锁的一个队列,每new一个condition,这个锁下面就多了一个队列,不同的队列中的线程可以精确控制。
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
char[] chars1 = "12345".toCharArray();
char[] chars2 = "ABCDE".toCharArray();
new Thread(()->{
try {
lock.lock();
for (int i = 0; i < chars1.length; i++) {
System.out.print(chars1[i]);
condition2.signal(); // 唤醒第二个线程,
condition1.await(); // 阻塞当前线程
}
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
},"t1").start();
new Thread(()->{
try {
lock.lock();
for (int i = 0; i < chars2.length; i++) {
System.out.print(chars2[i]);
condition1.signal(); // 唤醒第一个线程
condition2.await(); // 阻塞当前线程
}
condition1.signal();
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
},"t2").start();
}
解决方法三:
LockSupport
static Thread t1 = null,t2 = null;
public static void main(String[] args) {
char[] chars1 = "123456".toCharArray();
char[] chars2 = "ABCDEF".toCharArray();
t1 = new Thread(()->{
for (char a:chars1) {
System.out.print(a);
LockSupport.unpark(t2);//唤醒t2
LockSupport.park();//阻塞本线程
}
});
t2 = new Thread(()->{
for(char b:chars2){
LockSupport.park();
System.out.print(b);
LockSupport.unpark(t1);
}
});
t1.start();
t2.start();
}
其中的方法二和方法三较好,原因在于通过适当的修改,可以实现更多的线程之间交替输出