1.多個(gè)線程同時(shí)讀寫(xiě),讀線程的數(shù)量遠(yuǎn)遠(yuǎn)大于寫(xiě)線程,你認(rèn)為應(yīng)該如何解決并發(fā)的問(wèn)題?你會(huì)選擇什么樣的鎖?
答:解決高并發(fā)問(wèn)題:選擇ReadWriteLock讀寫(xiě)鎖。
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue queue = new Queue();
for(int i =1;i<=3;i++){
new Thread(new Runnable() {
public void run() {
while(true){
try {
Thread.sleep((long)Math.random()*100000);
queue.put(new Random().nextInt(100000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
while(true){
try {
Thread.sleep((long)Math.random()*100000);
queue.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
}
class Queue{
//共享數(shù)據(jù),只能有一個(gè)線程對(duì)其能更改
private Object data = 85;
ReadWriteLock rwl = new ReentrantReadWriteLock();
public void get(){
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data !");
System.out.println(Thread.currentThread().getName() + " have read data :" + data);
}finally{
rwl.readLock().unlock();
}
}
public void put(Object data){
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data !");
this.data = data ;
System.out.println(Thread.currentThread().getName() + " have write data :" + data);
}finally{
rwl.writeLock().unlock();
}
}
}
2.JAVA的AQS是否了解,它是干嘛的?
答:AbstractQueuedSynchronizer,抽象的隊(duì)列式的服務(wù)器,AQS定義了一套多線程訪問(wèn)共享資源的同步器框架,許多同步類(lèi)實(shí)現(xiàn)都依賴(lài)于它,如常用的ReentrantLock/Semaphore/CountDownLatch。
3.除了synchronized關(guān)鍵字之外,你是怎么來(lái)保障線程安全的?
答: 每次查詢(xún)少查點(diǎn),用rowid記錄標(biāo)記位,下次查詢(xún)從標(biāo)記位開(kāi)始,就是個(gè)變相的分頁(yè)。
4.Tomcat本身的參數(shù)你一般會(huì)怎么調(diào)整?
答:tomcat一些默認(rèn)參數(shù)不適合生產(chǎn)環(huán)境使用,因此需要修改一些參數(shù)。
①.修改啟動(dòng)時(shí)內(nèi)存參數(shù),并指定JVM時(shí)區(qū):
在Tomcat上運(yùn)行j2ee項(xiàng)目代碼時(shí),經(jīng)常會(huì)出現(xiàn)內(nèi)存溢出的情況,解決辦法是在系統(tǒng)參數(shù)中增加系統(tǒng)參數(shù):
window下,在catalina.bat最前面:
set JAVA_OPTS=-XX:PermSize=64M -XX:MaxPermSize=128m -Xms512m -Xmx1024m;-Duser.timezone=GMT+08;一定加在catalina.bat最前面。
linux下,在catalina.sh最前面添加:
JAVA_OPTS=“-XX:PermSize=64M -XX:MaxPermSize=128m -Xms512m -Xmx1024m; -Duser.timezone=Asia/Shanghai”;一定要加在catalina.bat最前面;
注意:前后二者區(qū)別,有無(wú)set,有無(wú)雙引號(hào)。
②.線程池配置:
使用線程池,用較少的線程處理較多的訪問(wèn),可以提高Tomcat處理請(qǐng)求的能力,使用方式:
首先,打開(kāi)/conf/server.xml,增加<Exector name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" minSpareThreads="20" maxldleTime="60000"/>
最大線程500,最小空閑線程數(shù)20,線程最大空閑時(shí)間60秒。
然后,修改<Connector ...>節(jié)點(diǎn),增加executor屬性,如:
<Connector
exector="tomcatThreadPool"
port="80"
protocol="HTTP/1.1"
maxThreads="600"
minSpareThreads="100"
maxSpareThreads="300"
connectionTimeout="60000"
keepAliveRequests="1"
redirectPort="443"/>
Tomcat可創(chuàng)建的最大線程數(shù),每一個(gè)線程處理一個(gè)請(qǐng)求;
Tomcat啟動(dòng)時(shí)的初始化的線程數(shù);
Tomcat就會(huì)關(guān)閉不再需要的socket線程;
connectionTimeout:網(wǎng)絡(luò)連接超時(shí),單位:毫秒。設(shè)置為0表示永不超時(shí),這樣設(shè)置有隱患的。通常設(shè)置為30000毫秒。
enableLookups:是否允許DNS查詢(xún)
注意:可以多個(gè)connector公用一個(gè)線程池。
③.調(diào)整連接相關(guān)Connector的參數(shù):
<Connector
executor="tomcatThreadPool"
port="80"
protocol="HTTP/1.1"
connectionTimeout="60000"
keepAliveTimeout="15000"
maxKeepAliveRequests="1"
redirectPort="443"
maxHttpHeaderSize="8129"
URIEncoding="UTF-8"
enableLookups="false" acceptCount="100"
disableUploadTimeout="true"/>
④.負(fù)載均衡,集群的配置
Tomcat6支持分布式部署,可以實(shí)現(xiàn)集群功能,提高相應(yīng)能力
⑤.利用JMX監(jiān)控Tomcat運(yùn)行情況,需要手工調(diào)整啟動(dòng)參數(shù)
打開(kāi)catalina.bat,增加一行
set JAVA_OPTS=%JAVA_OPTS%
-Dcom.sun.management.jmxremote.port=10090
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"
linux下修改cataline.sh:
JAVA_OPTS="-Dcom.sun.management.jmxremote.port=10090
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=%CATALINA_BASE\conf\logging.properties"
注意JDK\jre\lib\management\management.properties文件必須存在。重新啟動(dòng)Tomcat節(jié)點(diǎn),然后用jconsole連接
⑥.Tomcat增加一個(gè)應(yīng)用
在server.xml的Host標(biāo)簽中增加行
<Context displayName="OA" docBase="/app/web-apps/GACWP" path=""/>
path表示上下文名稱(chēng),空表示根路徑
5.你有沒(méi)有用過(guò)Spring的AOP? 是用來(lái)干嘛的? 大概會(huì)怎么使用?
答:常用的AOP及時(shí)安全檢驗(yàn),日志操作,事務(wù)操作等。
假如沒(méi)有aop,在做日志處理的時(shí)候,我們會(huì)在每個(gè)方法中添加日志處理;
但大多數(shù)的日志處理代碼是相同的,為了實(shí)現(xiàn)代碼復(fù)用,我們可能把日志處理抽離成一個(gè)新的方法。
但是這樣我們?nèi)匀槐仨毷謩?dòng)插入這些方法。
但這樣兩個(gè)方法就是強(qiáng)耦合的,假如此時(shí)我們不需要這個(gè)功能了,或者想換成其他功能,那么就必須一個(gè)個(gè)修改。
通過(guò)動(dòng)態(tài)代理,可以在指定位置執(zhí)行對(duì)應(yīng)流程。這樣就可以將一些橫向的功能抽離出來(lái)形成一個(gè)獨(dú)立的模塊,然后在指定位置插入這些功能。
這樣的思想,被稱(chēng)為面向切面編程,亦即AOP。
為了在指定位置執(zhí)行這些橫向的功能,需要知道指定的是什么地方,把切點(diǎn)和通知合在一起就是切面了,
一個(gè)切面指定了在何時(shí)何地執(zhí)行何種方法。在spring aop中如此定義這個(gè)切面:
@Aspect
@Component
public class UserAspect {
@Before("execution(* com.aop.service.impl.UserServiceImpl.login(..))")
public void loginLog(){
System.out.println("user login");
}
}
使用注解@Aspect將某個(gè)特定的類(lèi)聲明為切面,這樣,該類(lèi)下的方法就可以聲明為橫向的功能點(diǎn)后插入到指定位置。
使用execution表達(dá)式聲明在這個(gè)切點(diǎn),第一個(gè)位置指定了方法的返回值,*號(hào)代表任意類(lèi)型的返回值,
然后是所在的類(lèi)和方法名,*號(hào)同樣代表任意,就是該類(lèi)中任意的方法,在上一個(gè)例子中方法名是login,
則是指定了該類(lèi)中的login方法。然后最后一個(gè)參數(shù)是方法入?yún)?,因?yàn)閖ava中支持重載,
所以這個(gè)參數(shù)可以幫助你更精確的進(jìn)行定位。兩點(diǎn)表示任意參數(shù)類(lèi)型。
這樣,execution表達(dá)式告訴了程序該在何地執(zhí)行通知。
而被諸如@Before注解修飾的方法就是通知的內(nèi)容,也就是做什么。
至此,我們就可以使用spring aop,但是還有兩點(diǎn)需要得到注意
1. 將切面類(lèi)聲明為一個(gè)bean
2. 切點(diǎn)指定的方法所在的類(lèi)也同樣需由spring注入才能生效
6.如果一個(gè)接口有2個(gè)不同的實(shí)現(xiàn), 那么怎么來(lái)Autowire一個(gè)指定的實(shí)現(xiàn)?
//使用@Qualifier("aaaService")注解
@Service
public class AaaService implements IChangePassword {
@Override
public void changePassword(String username, String password) {}
}
@Service
public class BbbService implements IChangePassword {
@Override
public void changePassword(String username, String password) {}
}
public class AccountController extends BaseController {
@Autowired
@Qualifier("aaaService")
private IChangePassword aaaService;
@Autowired
@Qualifier("bbbService")
private IChangePassword bbbService;
}
7.如果想在某個(gè)Bean生成并裝配完畢后執(zhí)行自己的邏輯,可以什么方式實(shí)現(xiàn)?
答:有時(shí),我們需要在啟動(dòng)bean時(shí)初始化bean屬性,例如讀取perporties文件,對(duì)屬性進(jìn)行賦值;啟動(dòng)容器時(shí)讓某個(gè)method方法執(zhí)行等等。這時(shí)需要在進(jìn)行配置,讓bean在注入時(shí)啟動(dòng)指定方法。
共有以下幾種方法:
①、如果是通過(guò)XML配置文件進(jìn)行Bean的生成,我們可以在配置Bean的時(shí)候,使用init-method=“executionMethod”屬性,這樣在當(dāng)前Bean實(shí)例化完成后,就會(huì)自動(dòng)執(zhí)行指定的executionMethod。executionMethod為定義在Bean中的一個(gè)方法。
<bean id="initializingBean" class="全類(lèi)名" init-method="executionMethod"></bean>
②、可以讓Bean實(shí)現(xiàn)InitializationBean接口,并重寫(xiě)其afterPropertiesSet()方法。
③、給需要調(diào)用的方法加上@PostConstruct注解,即在構(gòu)造方法后調(diào)用。比如
@PostConstruct
private void initMethod1(){ .....}
7.SpringBoot沒(méi)有放到web容器里為什么能跑HTTP服務(wù)?
答:因?yàn)閟pringboot中內(nèi)嵌了tomcat,jetty,undertow。
8.SpringBoot中如果你想使用自定義的配置文件而不僅僅是application.properties,應(yīng)該怎么弄?
答:①.在類(lèi)中使用注解 @PropertySource,例如:@PropertySource("classpath:define.properties")
②.在配置文件中引入配置文件<context:property-placeholder location="classpath:jdbc.properties,classpath:rabbitmq.properties"/>
9.SpringMVC如果希望把輸出的Object(例如XXResult或者XXResponse)這種包裝為JSON輸出, 應(yīng)該怎么處理?
答:①.加入jackson依賴(lài)的jar包:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
②.配置文件中進(jìn)行配置:
<bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<value>application/json;charset=UTF-8</value>
</property
</bean>
③.直接使用注解的方式“@ResponseBody”自動(dòng)將返回值轉(zhuǎn)化為json格式.
10.如果有很多數(shù)據(jù)插入MYSQL 你會(huì)選擇什么方式?
答:在MySQL的命令行界面執(zhí)行以下命令:LOAD DATA INFILE 'd:/t.sql' INTO TABLE e_tuike_goods FIELDS TERMINATED BY ',';
11.如果查詢(xún)很慢,你會(huì)想到的第一個(gè)方式是什么?索引是干嘛的?
答:sql語(yǔ)句優(yōu)化或者該數(shù)據(jù)表添加索引,
就比如一本書(shū),你想看第六章第六節(jié)講的是什么,你會(huì)怎么做,一般人肯定去看目錄,
找到這一節(jié)對(duì)應(yīng)的頁(yè)數(shù),然后翻到這一頁(yè)。這就是目錄索引,幫助讀者快速找到想要的章節(jié)。
在數(shù)據(jù)庫(kù)中,我們也有索引,其目的當(dāng)然和我們翻書(shū)一樣,能幫助我們提高查詢(xún)的效率。
索引就像目錄一樣,減少了計(jì)算機(jī)工作量,對(duì)于表記錄較多的數(shù)據(jù)庫(kù)來(lái)說(shuō)是非常實(shí)用的,
可以大大的提高查詢(xún)的速度。否則的話,如果沒(méi)有索引,計(jì)算機(jī)會(huì)一條一條的掃描,
每一次都要掃描所有的記錄,浪費(fèi)大量的cpu時(shí)間。
我們都知道對(duì)于一個(gè)無(wú)序的表,和一個(gè)有序的表,有序表的查詢(xún)方法會(huì)有更多地選擇,
每種查詢(xún)方法的效率也不同,其實(shí)為表建立索引,也就是對(duì)表中的記錄按照索引字段排序。
12.查詢(xún)死掉了,想要找出執(zhí)行的查詢(xún)進(jìn)程用什么命令?
答: ps S 列出程序時(shí),包括已中斷的子程序資料。
13.讀寫(xiě)分離是怎么做的?你認(rèn)為中間件會(huì)怎么來(lái)操作?這樣操作跟事務(wù)有什么關(guān)系?
答:①.讀寫(xiě)分離的實(shí)現(xiàn)原理就是在執(zhí)行SQL語(yǔ)句的時(shí)候,判斷到底是讀操作還是寫(xiě)操作,把讀的操作轉(zhuǎn)向到讀的服務(wù)器上(從服務(wù)器,一般是多臺(tái)),寫(xiě)的操作轉(zhuǎn)到寫(xiě)的服務(wù)器上(主服務(wù)器,一般是一臺(tái)),當(dāng)然為了保證多臺(tái)數(shù)據(jù)庫(kù)數(shù)據(jù)的一致性,需要主從復(fù)制。
主從復(fù)制的實(shí)現(xiàn)原理是:mysql中有一種日志,叫做bin日志(二進(jìn)制日志),會(huì)記錄下所有修改過(guò)數(shù)據(jù)庫(kù)的sql語(yǔ)句。
主從復(fù)制的原理實(shí)際是多臺(tái)服務(wù)器都開(kāi)啟bin日志,然后主服務(wù)器會(huì)把執(zhí)行過(guò)的sql語(yǔ)句記錄到bin日志中,之后從服務(wù)器讀取這個(gè)bin日志,把該日志的內(nèi)容保存到自己中繼日志里面,從服務(wù)器再把中繼日志中記錄的sql語(yǔ)句同樣的執(zhí)行一遍,這樣從服務(wù)器上的數(shù)據(jù)就和主服務(wù)器相同了。
②.中間件有淘寶開(kāi)源的cobar,以及后來(lái)開(kāi)源社區(qū)根據(jù)cobar進(jìn)行二次開(kāi)發(fā)的mycat
14.你知道哪些或者你們線上使用什么GC策略? 它有什么優(yōu)勢(shì),適用于什么場(chǎng)景?
使用SerialGC的場(chǎng)景:
1、如果應(yīng)用的堆大小在100MB以?xún)?nèi)。
2、如果應(yīng)用在一個(gè)單核單線程的服務(wù)器上面,并且對(duì)應(yīng)用暫停的時(shí)間無(wú)需求。
使用ParallelGC的場(chǎng)景:
如果需要應(yīng)用在高峰期有較好的性能,但是對(duì)應(yīng)用停頓時(shí)間無(wú)高要求(比如:停頓1s甚至更長(zhǎng))。
使用G1、CMS場(chǎng)景:
1、對(duì)應(yīng)用的延遲有很高的要求。
2、如果內(nèi)存大于6G請(qǐng)使用G1。
15.JAVA類(lèi)加載器包括幾種?它們之間的父子關(guān)系是怎么樣的?雙親委派機(jī)制是什么意思?有什么好處?
答:java類(lèi)加載器包括:
1.啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader),也叫跟類(lèi)加載器,負(fù)責(zé)加載java的核心類(lèi)庫(kù),例如(%JAVA_HOME%/lib)目錄下的rt.jar(包含System,String這樣的核心類(lèi)),跟類(lèi)加載器非常特殊,它不是java.lang.ClassLoader的子類(lèi),它是JVM自身內(nèi)部由C/C++實(shí)現(xiàn)的,并不是java實(shí)現(xiàn)的。
2.擴(kuò)展類(lèi)加載器(Extension ClassLoader),負(fù)責(zé)加載擴(kuò)展目錄(%JAVA_HOME%/jre/lib/ext)下的jar包,用戶(hù)可以把自己開(kāi)發(fā)的類(lèi)打包成jar包放在這個(gè)目錄下即可擴(kuò)展核心類(lèi)以外的功能
3.系統(tǒng)類(lèi)加載器(System ClassLoader\APP ClassLoader),又稱(chēng)為應(yīng)用程序類(lèi)加載器,是加載CLASSPATH環(huán)境變量下所指定的jar包與類(lèi)路徑,一般來(lái)說(shuō),用戶(hù)自定義的就是由APP ClassLoader加載的
各類(lèi)加載器之間的關(guān)系:
以結(jié)合關(guān)系復(fù)用父類(lèi)加載器的父子關(guān)系,注意,這里的父子關(guān)系并不是繼承關(guān)系實(shí)現(xiàn)的
類(lèi)加載器的雙親委派加載機(jī)制:
當(dāng)一個(gè)類(lèi)收到了類(lèi)加載請(qǐng)求,他首先不會(huì)嘗試自己去加載這個(gè)類(lèi),而是把這個(gè)請(qǐng)求委派給父類(lèi)去完成,每一個(gè)層次類(lèi)加載都是如此,因此所有的加載請(qǐng)求都應(yīng)該傳送到啟動(dòng)類(lèi)加載器中,只有當(dāng)父類(lèi)加載器反饋?zhàn)约簾o(wú)法完成這個(gè)請(qǐng)求的時(shí)候(在他的加載路徑里找不到這個(gè)所需要加載的類(lèi)),子類(lèi)加載器才會(huì)嘗試自己去加載。
雙親委派模型的源碼實(shí)現(xiàn):
主要體現(xiàn)在ClassLoader的loadClass()方法,思路很簡(jiǎn)單:先檢查是否已經(jīng)被加載,若沒(méi)有被加載則調(diào)用父類(lèi)的LoadClass()方法,若父類(lèi)加載器為空,則默認(rèn)使用啟動(dòng)類(lèi)加載器作為父類(lèi)加載器,如果父類(lèi)加載器加載失敗,拋出ClassNotFoundException異常后,調(diào)用自己的findClass()方法進(jìn)行加載。
16.如何自定義一個(gè)類(lèi)加載器?你使用過(guò)哪些或者你在什么場(chǎng)景下需要一個(gè)自定義的類(lèi)加載器嗎?堆內(nèi)存設(shè)置的參數(shù)是什么?
答:我們需要的類(lèi)不一定存放在已經(jīng)設(shè)置好的ClassPath下(有系統(tǒng)類(lèi)加載器APPClassLoader加載的路徑),對(duì)于自定義路徑下的class類(lèi)文件的加載,我們需要自己的ClassLoader。
有時(shí)我們不一定是從類(lèi)文件中讀取類(lèi),可能是從網(wǎng)絡(luò)的輸入流中讀取類(lèi),這就需要做一些加密和解密操作,這就需要自己實(shí)現(xiàn)加載類(lèi)的邏輯,當(dāng)然其他的特殊處理也同樣適用。
可以定義類(lèi)的實(shí)現(xiàn)機(jī)制,實(shí)現(xiàn)類(lèi)的熱部署,如OSGi中的bundle模塊就是通過(guò)實(shí)現(xiàn)自己的ClassLoader實(shí)現(xiàn)的。
public class MyClassLoader extends ClassLoader {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (args.length == 0) {
System.out.println("沒(méi)有類(lèi)啊");
}
// 取出第一個(gè)參數(shù),就是需要運(yùn)行的類(lèi)
String procressClass = args[0];
// 剩余參數(shù)為運(yùn)行目標(biāo)類(lèi)的參數(shù),將這些參數(shù)復(fù)制到一個(gè)新數(shù)組中
String[] procress = new String[args.length - 1];
System.arraycopy(args, 1, procress, 0, procress.length);
MyClassLoader myClassLoader = new MyClassLoader();
Class<?> class1 = myClassLoader.loadClass(procressClass);
Method main = class1.getMethod("main", (new
String[0]).getClass());
Object argsArray[] = { procress };
main.invoke(null, argsArray);
}
/**
* @TODO 讀取文件內(nèi)容
*/
public byte[] getBytes(String fileName) {
File file = new File(fileName);
long len = file.length();
byte[] raw = new byte[(int) len];
try {
FileInputStream fileInputStream =
new FileInputStream(file);
try {
int r = fileInputStream.read(raw);
fileInputStream.close();
if (r != len)
throw new IOException("fail to read
the file...");
} catch (IOException e) {
e.printStackTrace();
}
return raw;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* @TODO 編譯java文件
*/
public boolean complie(String javaFile) {
System.out.println("正在編譯...");
Process process = null;
try {
process = Runtime.getRuntime().exec("javac " + javaFile);
try {
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
int result = process.exitValue();
return result == 0;
}
/**
* @TODO 關(guān)鍵,重寫(xiě)findClass方法
*/
@Override
protected Class<?> findClass(String arg0) throws ClassNotFoundException {
Class<?> class1 = null;
String filePath = arg0.replaceAll(".", "/");
String className = filePath + ".class";
String javaName = filePath + ".java";
File javaFile = new File(javaName);
File classFile = new File(className);
if (javaFile.exists()
&& (!classFile.exists() || javaFile.lastModified() > classFile .lastModified())) {
if (!complie(javaName) || !classFile.exists()) {
throw new ClassNotFoundException(javaName + " Class找不到");
}
}
if (classFile.exists()) {
byte[] raw = getBytes(className);
class1 = defineClass(arg0, raw, 0, raw.length);
}
if (class1 == null) {
throw new ClassNotFoundException(javaName + " 加載失敗");
}
return class1;
}
}
17.HashMap和Hashtable的區(qū)別。
答:HashMap和Hashtable都實(shí)現(xiàn)了Map接口,但決定用哪一個(gè)之前先要弄清楚它們之間的區(qū)別,主要的區(qū)別有:線程安全性,同步(synchronization)以及速度。
1.HashMap是非synchronized的,并可以接收null,HashMap可以接受為null的鍵(key)和值(value),而Hashtable則不行。
2.Hashtable是線程安全的,多個(gè)線程是可以共享一個(gè)Hashtable,而如果沒(méi)有正確的同步的話,多個(gè)線程是不能共享HashMap的,java5提供了ConcurrentHashMap,它是HashTable的替代,擴(kuò)展性更好。
3.HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的,所以當(dāng)有其他線程改變了HashMap的結(jié)構(gòu),將會(huì)拋出ConcurrentModificationException。
4.由于Hashtable是線程安全的,所以在單線程環(huán)境下他比HashMap要慢,如果不需要同步,只需要單一線程,那么使用HashMap性能要好過(guò)Hashtable。
注意:
1.synchronized意味著在一次僅有一個(gè)線程能夠更改Hashtable,就是說(shuō)任何線程要更新Hashtable時(shí)要首先獲得同步鎖,其他線程要等到同步鎖被釋放之后才能再次獲得同步鎖更新Hashtable。
2.使HashMap同步:
Map m = Collections.synchronizeMap(hashMap);
3.僅在需要線程安全的時(shí)候使用HashTable,使用java5或以上的話,使用ConcurrentHashMap。