淘宝面试题:
实现一个容器,提供两个方法,add,size,写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当到5个时候,线程2给出提示并结束。
很明显是一个多线程通信问题。
方法1:通过wait-notify,重点:1)wait/await操作释放锁,notify/sinal不释放锁,wait后面的代码要想执行必须获得锁才可以执行 2)wait/await一定要在notify/signal之前执行,如果notify/signal在wait/await之前则无效
import java.util.ArrayList;
import java.util.List;
public class TaobaoTest {
public static void main(String[] args) {
Container container = new Container();
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("t2启动");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("判断是第五个打印");
lock.notify(); //唤醒线程t1
}
return;
},"t2").start();
new Thread(() -> {
synchronized (lock) {
System.out.println("t1启动");
try {
for (int i = 0; i < 10; i++) {
if (container.size() == 5) {
lock.notify(); //唤醒t2,但是还没有释放锁,所以t2无法执行
System.out.println("叫t1打印");
lock.wait(); //t1线程睡眠,释放锁,t2线程可以执行
}
container.add("a");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1").start();
}
}
/**
* 容器
*/
class Container {
private volatile List<String> list = new ArrayList<>();
public void add(String a) {
list.add(a);
}
public int size() {
return list.size();
}
}
方法2:CountDownLatch
public class HelloService {
public static void main(String[] args) throws Exception {
Container container = new Container();
final CountDownLatch latch = new CountDownLatch(1);
new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
}
System.out.println("判断是第五个打印");
},"t2").start();
Thread.sleep(500);
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("a");
container.add("a");
if (container.size() == 5) {
latch.countDown();
}
try {
Thread.sleep(300);
} catch (Exception e) {
}
}
},"t1").start();
}
}
/**
* 容器
*/
class Container {
private volatile List<String> list = new ArrayList<>();
public void add(String a) {
list.add(a);
}
public int size() {
return list.size();
}
}
去掉t1线程中的Thread.sleep(300),那就需要在增加一个CountDownLatch,如下:
public static void main(String[] args) throws Exception {
Container container = new Container();
//两个CountDownLatch去掉强制Thread
final CountDownLatch latch = new CountDownLatch(1);
final CountDownLatch latch2 = new CountDownLatch(1);
new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
}
latch2.countDown();
System.out.println("判断是第五个打印");
},"t2").start();
Thread.sleep(500);
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
System.out.println("a");
container.add("a");
if (container.size() == 5) {
latch.countDown();
latch2.await();
}
}
} catch (InterruptedException e) {
}
},"t1").start();
}
方法3:LockSupport,线程t2必须先执行
public class HelloService {
static Thread t1 = null, t2 = null;
public static void main(String[] args) throws Exception {
Container container = new Container();
t2 = new Thread(() -> {
LockSupport.park();
System.out.println("判断是第五个打印");
LockSupport.unpark(t1);
},"t2");
t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("a");
container.add("a");
if (container.size() == 5) {
LockSupport.unpark(t2);
LockSupport.park();
}
}
},"t1");
t2.start();//必须保证t2先执行
Thread.sleep(100);
t1.start();
}
}
面试题:两个线程交互打印,最终输出结果为A1B2C3...Z26
1、wait/notify
public class SwitchPrintX {
static public Object o = new Object();
static class PrintChar implements Runnable {
public void run() {
for (char c = 'A'; c <= 'Z'; c++) {
synchronized(o) {
System.out.print(c);
o.notify();
try {
o.wait();
} catch (InterruptedException e) {
}
}
}
}
}
static class PrintNumber implements Runnable {
public void run() {
for (int i = 1; i <= 26; i++) {
synchronized(o) {
try {
o.wait();
} catch (InterruptedException e) {
}
System.out.print(i);
o.notify();
}
}
}
}
public static void main(String[] args) throws Exception {
//print线程先执行, 因为如果不执行,可能会出现notify在wait之前,无法唤醒线程
new Thread(new PrintNumber()).start();
Thread.sleep(500);
new Thread(new PrintChar()).start();
return;
}
}
2、Lock和Condition
public class SwitchPrintX {
static public ReentrantLock lock = new ReentrantLock();
static public Condition charCondition = lock.newCondition();
static public Condition numCondition = lock.newCondition();
static class PrintChar implements Runnable {
public void run() {
for (char c = 'A'; c <= 'Z'; c++) {
lock.lock();
System.out.print(c);
numCondition.signal();
try {
charCondition.await();
} catch (InterruptedException e) {
}
lock.unlock();
}
}
}
static class PrintNumber implements Runnable {
public void run() {
for (int i = 1; i <= 26; i++) {
lock.lock();
try {
numCondition.await();
} catch (InterruptedException e) {
}
System.out.print(i);
charCondition.signal();
lock.unlock();
}
}
}
public static void main(String[] args) throws Exception {
//print线程先执行, 因为如果不执行,可能会出现signal在await之前,无法唤醒线程
new Thread(new PrintNumber()).start();
Thread.sleep(500);
new Thread(new PrintChar()).start();
return;
}
}