一道关于线程相关的面试题


一道关于线程相关的面试题


面试的时候,面中多线程,线程安全的概率蛮大的。笔者之间就遇到这到面试题,今突然想到这该死的面试题,还好忙里偷闲,拿来重新思考。

面试题

实现一个容器,提供两个方法,分别为:1、add()用于添加元素。2、size()用于获取容器长度。使用多线程实现两个要求:

1,线程1添加10个元素到容器中,

2,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束

以下给出3种方案

第一种方案

考察知识点:volatile 关键字,请看代码:

 public class Demo01 {
 List<object> list = new ArrayList<>();/<object>
 /**
* 1、add()用于添加元素。
* @param o
*/
public void addObject(Object o){
list.add(o);

}
 /**
* 2、size()用于获取容器长度
* @return
*/
public int getSize(){
return list.size();
}
 public static void main(String[] args) {
Demo01 demo01 = new Demo01();
 //线程t2用于监控容器长度
new Thread(()->{
System.out.println("t2线程启动,开始监控...");
while(true){
if(demo01.getSize() == 5){
break;
}
}
System.out.println("当前容器长度为:" + demo01.getSize()+",t2线程结束...");
},"t2").start();
 try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
 new Thread(()->{
System.out.println("t1线程启动,开始添加元素...");
for(int i=0;i<10;i++){
demo01.addObject(new Object());
System.out.println("当前容器长度为:" + demo01.getSize());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1").start();
 } 

}

最后执行,结果:

一道关于线程相关的面试题

什么情况??没监控到,并且线程t2一直在运行。。。原因:这里list在两个线程之间不保证可见性,所以线程t2始终结束不了(也许会结束,但是,不知道什么时候能结束)。

因此要将 List list = new ArrayList<>() 改为 volatile List list = new ArrayList<>() 即可。

一道关于线程相关的面试题

再一次输出:

一道关于线程相关的面试题

此种方案,利用了volatile可见性的特点。但是这种方式浪费cpu资源。

第二种方案

加锁机制,见如下代码:

 public class Demo2 {
 volatile List<object> list = new ArrayList<>();/<object>
 /**
* 1、add()用于添加元素。
* @param o
*/
public void addObject(Object o){
list.add(o);
}
 /**
* 2、size()用于获取容器长度
* @return
*/
public int getSize(){
return list.size();
}
 public static void main(String[] args) {
Demo2 demo2 = new Demo2();
Object lock = new Object();
 new Thread(()->{
synchronized (lock){
System.out.println("t2线程启动,开始监控...");
if(demo2.getSize() != 5){
try {
lock.wait();//释放锁,让t1运行
} catch (InterruptedException e) {
e.printStackTrace();
}
}

lock.notify();//唤醒t1
System.out.println("当前容器长度为:" + demo2.getSize()+",t2线程结束...");
}
 },"t2").start();
 try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
 new Thread(()->{
synchronized (lock){
for(int i=0;i<10;i++){
demo2.addObject(new Object());
System.out.println("当前容器长度为:" + demo2.getSize());
if(demo2.getSize() == 5){
lock.notify();//唤醒t2
try {
lock.wait();//释放锁,让t2执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
 }
}
 },"t1").start();
 }
}

输出结果:

一道关于线程相关的面试题

这种方案用synchronized加wait,notify就显得太重了

第三种方案

CountDownLatch发令枪机制,见如下代码:

 public class Demo3 {
 volatile List<object> list = new ArrayList<>();/<object>
 /**
* 1、add()用于添加元素。
* @param o
*/
public void addObject(Object o){
list.add(o);
}
 /**
* 2、size()用于获取容器长度
* @return
*/
public int getSize(){
return list.size();
}
 public static void main(String[] args) {
Demo3 demo3 = new Demo3();
CountDownLatch countDownLatch = new CountDownLatch(5);
 new Thread(()->{
System.out.println("t2线程启动,开始监控...");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前容器长度为:" + demo3.getSize()+",t2线程结束...");
},"t2").start();
 try {
TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {
e.printStackTrace();
}
 new Thread(()->{
for(int i=0;i<10;i++){
demo3.addObject(new Object());
countDownLatch.countDown();
System.out.println("当前容器长度为:" + demo3.getSize());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1").start();
 }
 }

这种使用 CountDownLatch方式,相当于是发令枪,运动员线程调用await等待,计数到0开始运行。其实就是 使用await和countdown方法替代wait和notify。 CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行

以上就是三个方案,当然如果很熟练多线程并发编程的话,还有很多种方案。

本人水平有限,难免有错误或遗漏之处,望大家指正和谅解,提出宝贵意见,愿与之交流。


分享到:


相關文章: