一道關於線程相關的面試題


一道關於線程相關的面試題


面試的時候,面中多線程,線程安全的概率蠻大的。筆者之間就遇到這到面試題,今突然想到這該死的面試題,還好忙裡偷閒,拿來重新思考。

面試題

實現一個容器,提供兩個方法,分別為: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的值為零時當前線程繼續運行

以上就是三個方案,當然如果很熟練多線程併發編程的話,還有很多種方案。

本人水平有限,難免有錯誤或遺漏之處,望大家指正和諒解,提出寶貴意見,願與之交流。


分享到:


相關文章: