原文:http://blog.csdn.net/xiao__gui/article/details/8934832
ArrayList和Vector有什么區(qū)別?HashMap和HashTable有什么區(qū)別?StringBuilder和StringBuffer有什么區(qū)別?這些都是Java面試中常見(jiàn)的基礎(chǔ)問(wèn)題。面對(duì)這樣的問(wèn)題,回答是:ArrayList是非線(xiàn)程安全的,Vector是線(xiàn)程安全的;HashMap是非線(xiàn)程安全的,HashTable是線(xiàn)程安全的;StringBuilder是非線(xiàn)程安全的,StringBuffer是線(xiàn)程安全的。因?yàn)檫@是昨晚剛背的《Java面試題大全》上面寫(xiě)的。此時(shí)如果繼續(xù)問(wèn):什么是線(xiàn)程安全?線(xiàn)程安全和非線(xiàn)程安全有什么區(qū)別?分別在什么情況下使用?這樣一連串的問(wèn)題,一口老血就噴出來(lái)了…
非線(xiàn)程安全的現(xiàn)象模擬
這里就使用ArrayList和Vector二者來(lái)說(shuō)明。
下面的代碼,在主線(xiàn)程中new了一個(gè)非線(xiàn)程安全的ArrayList,然后開(kāi)1000個(gè)線(xiàn)程分別向這個(gè)ArrayList里面添加元素,每個(gè)線(xiàn)程添加100個(gè)元素,等所有線(xiàn)程執(zhí)行完成后,這個(gè)ArrayList的size應(yīng)該是多少?應(yīng)該是100000個(gè)?
public class Main
{
public static void main(String[] args)
{
// 進(jìn)行10次測(cè)試
for(int i = 0; i < 10; i++)
{
test();
}
}
public static void test()
{
// 用來(lái)測(cè)試的List
List<Object> list = new ArrayList<Object>();
// 線(xiàn)程數(shù)量(1000)
int threadCount = 1000;
// 用來(lái)讓主線(xiàn)程等待threadCount個(gè)子線(xiàn)程執(zhí)行完畢
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 啟動(dòng)threadCount個(gè)子線(xiàn)程
for(int i = 0; i < threadCount; i++)
{
Thread thread = new Thread(new MyThread(list, countDownLatch));
thread.start();
}
try
{
// 主線(xiàn)程等待所有子線(xiàn)程執(zhí)行完成,再向下執(zhí)行
countDownLatch.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
// List的size
System.out.println(list.size());
}
}
class MyThread implements Runnable
{
private List<Object> list;
private CountDownLatch countDownLatch;
public MyThread(List<Object> list, CountDownLatch countDownLatch)
{
this.list = list;
this.countDownLatch = countDownLatch;
}
public void run()
{
// 每個(gè)線(xiàn)程向List中添加100個(gè)元素
for(int i = 0; i < 100; i++)
{
list.add(new Object());
}
// 完成一個(gè)子線(xiàn)程
countDownLatch.countDown();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
上面進(jìn)行了10次測(cè)試(為什么要測(cè)試10次?因?yàn)榉蔷€(xiàn)程安全并不是每次都會(huì)導(dǎo)致問(wèn)題)。
輸出結(jié)果:
99946
100000
100000
100000
99998
99959
100000
99975
100000
99996
上面的輸出結(jié)果發(fā)現(xiàn),并不是每次測(cè)試結(jié)果都是100000,有好幾次測(cè)試最后ArrayList的size小于100000,甚至?xí)r不時(shí)會(huì)拋出個(gè)IndexOutOfBoundsException異常。(如果沒(méi)有這個(gè)現(xiàn)象可以多試幾次)
這就是非線(xiàn)程安全帶來(lái)的問(wèn)題了。上面的代碼如果用于生產(chǎn)環(huán)境,就會(huì)有隱患就會(huì)有BUG了。
再用線(xiàn)程安全的Vector來(lái)進(jìn)行測(cè)試,上面代碼改變一處,test()方法中
List<Object> list = new ArrayList<Object>();
改為
List<Object> list = new Vector<Object>();
再運(yùn)行程序。
輸出結(jié)果:
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
再多跑幾次,發(fā)現(xiàn)都是100000,沒(méi)有任何問(wèn)題。因?yàn)閂ector是線(xiàn)程安全的,在多線(xiàn)程操作同一個(gè)Vector對(duì)象時(shí),不會(huì)有任何問(wèn)題。
再換成LinkedList試試,同樣還會(huì)出現(xiàn)ArrayList類(lèi)似的問(wèn)題,因?yàn)長(zhǎng)inkedList也是非線(xiàn)程安全的。
二者如何取舍
非線(xiàn)程安全是指多線(xiàn)程操作同一個(gè)對(duì)象可能會(huì)出現(xiàn)問(wèn)題。而線(xiàn)程安全則是多線(xiàn)程操作同一個(gè)對(duì)象不會(huì)有問(wèn)題。
線(xiàn)程安全必須要使用很多synchronized關(guān)鍵字來(lái)同步控制,所以必然會(huì)導(dǎo)致性能的降低。
所以在使用的時(shí)候,如果是多個(gè)線(xiàn)程操作同一個(gè)對(duì)象,那么使用線(xiàn)程安全的Vector;否則,就使用效率更高的ArrayList。
非線(xiàn)程安全!=不安全
有人在使用過(guò)程中有一個(gè)不正確的觀點(diǎn):我的程序是多線(xiàn)程的,不能使用ArrayList要使用Vector,這樣才安全。
非線(xiàn)程安全并不是多線(xiàn)程環(huán)境下就不能使用。注意我上面有說(shuō)到:多線(xiàn)程操作同一個(gè)對(duì)象。注意是同一個(gè)對(duì)象。比如最上面那個(gè)模擬,就是在主線(xiàn)程中new的一個(gè)ArrayList然后多個(gè)線(xiàn)程操作同一個(gè)ArrayList對(duì)象。
如果是每個(gè)線(xiàn)程中new一個(gè)ArrayList,而這個(gè)ArrayList只在這一個(gè)線(xiàn)程中使用,那么肯定是沒(méi)問(wèn)題的。
線(xiàn)程安全的實(shí)現(xiàn)
線(xiàn)程安全是通過(guò)線(xiàn)程同步控制來(lái)實(shí)現(xiàn)的,也就是synchronized關(guān)鍵字。
在這里,我用代碼分別實(shí)現(xiàn)了一個(gè)非線(xiàn)程安全的計(jì)數(shù)器和線(xiàn)程安全的計(jì)數(shù)器Counter,并對(duì)他們分別進(jìn)行了多線(xiàn)程測(cè)試。
非線(xiàn)程安全的計(jì)數(shù)器:
public class Main
{
public static void main(String[] args)
{
// 進(jìn)行10次測(cè)試
for(int i = 0; i < 10; i++)
{
test();
}
}
public static void test()
{
// 計(jì)數(shù)器
Counter counter = new Counter();
// 線(xiàn)程數(shù)量(1000)
int threadCount = 1000;
// 用來(lái)讓主線(xiàn)程等待threadCount個(gè)子線(xiàn)程執(zhí)行完畢
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 啟動(dòng)threadCount個(gè)子線(xiàn)程
for(int i = 0; i < threadCount; i++)
{
Thread thread = new Thread(new MyThread(counter, countDownLatch));
thread.start();
}
try
{
// 主線(xiàn)程等待所有子線(xiàn)程執(zhí)行完成,再向下執(zhí)行
countDownLatch.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
// 計(jì)數(shù)器的值
System.out.println(counter.getCount());
}
}
class MyThread implements Runnable
{
private Counter counter;
private CountDownLatch countDownLatch;
public MyThread(Counter counter, CountDownLatch countDownLatch)
{
this.counter = counter;
this.countDownLatch = countDownLatch;
}
public void run()
{
// 每個(gè)線(xiàn)程向Counter中進(jìn)行10000次累加
for(int i = 0; i < 10000; i++)
{
counter.addCount();
}
// 完成一個(gè)子線(xiàn)程
countDownLatch.countDown();
}
}
class Counter
{
private int count = 0;
public int getCount()
{
return count;
}
public void addCount()
{
count++;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
上面的測(cè)試代碼中,開(kāi)啟1000個(gè)線(xiàn)程,每個(gè)線(xiàn)程對(duì)計(jì)數(shù)器進(jìn)行10000次累加,最終輸出結(jié)果應(yīng)該是10000000。
但是上面代碼中的Counter未進(jìn)行同步控制,所以非線(xiàn)程安全。
輸出結(jié)果:
9963727
9973178
9999577
9987650
9988734
9988665
9987820
9990847
9992305
9972233
稍加修改,把Counter改成線(xiàn)程安全的計(jì)數(shù)器:
class Counter
{
private int count = 0;
public int getCount()
{
return count;
}
public synchronized void addCount()
{
count++;
}
}
上面只是在addCount()方法中加上了synchronized同步控制,就成為一個(gè)線(xiàn)程安全的計(jì)數(shù)器了。再執(zhí)行程序。
輸出結(jié)果:
10000000
10000000
10000000
10000000
10000000
10000000
10000000
10000000
10000000
10000000
|