1.JVM內存模型相關概念html
2.Java多線程併發深刻理解java
3.Servlet、設計模式、SpringMVC深刻理解linux
4.Java基礎遺漏點補充程序員
數據庫鏈接池:JDBC connection pool,頻繁的創建、關閉鏈接,會極大的減低系統的性能,由於對於鏈接的使用成了系統性能的瓶頸。鏈接複用。經過創建一個數據庫鏈接池以及一些管理方法,使得一個數據庫鏈接能夠獲得高效、安全的複用,避免了數據庫鏈接頻繁創建、關閉的開銷。對於共享資源,有一個很著名的設計模式:資源池。該模式正是爲了解決資源頻繁分配、釋放所形成的問題的。把該模式應用到數據庫鏈接管理領域,就是創建一個數據庫鏈接池,提供一套高效的鏈接分配、使用策略,最終目標是實現鏈接的高效、安全的複用。數據庫鏈接池的基本原理是在內部對象池中維護必定數量的數據庫鏈接,並對外暴露數據庫鏈接獲取和返回方法。外部使用者可經過getConnection 方法獲取鏈接,使用完畢後再經過releaseConnection 方法將鏈接返回,注意此時鏈接並無關閉,而是由鏈接池管理器回收,併爲下一次使用作好準備。web
使用數據庫鏈接池的優點:資源重用<因爲數據庫鏈接獲得重用,避免了頻繁建立、釋放鏈接引發的大量性能開銷。在減小系統消耗的基礎上,另外一方面也增進了系統運行環境的平穩性(減小內存碎片以及數據庫臨時進程/線程的數量)。>更快的系統響應速度:<數據庫鏈接池在初始化過程當中,每每已經建立了若干數據庫鏈接置於池中備用。此時鏈接的初始化工做均已完成。對於業務請求處理而言,直接利用現有可用鏈接,避免了數據庫鏈接初始化和釋放過程的時間開銷,從而縮減了系統總體響應時間。>新的資源分配手段:<對於多應用共享同一數據庫的系統而言,可在應用層經過數據庫鏈接的配置,實現數據庫鏈接池技術。某一應用最大可用數據庫鏈接數的限制,避免某一應用獨佔全部數據庫資源。>統一的鏈接管理,避免數據庫鏈接泄漏:<在較爲完備的數據庫鏈接池實現中,可根據預先的鏈接佔用超時設定,強制收回被佔用鏈接。從而避免了常規數據庫鏈接操做中可能出現的資源泄漏。一個最小化的數據庫鏈接池實現.>面試
數據庫的遊標:遊標其實是一種能從包括多條數據記錄的結果集中每次提取一條記錄的機制。遊標由結果集和結果集中指向特定記錄的遊標位置組成。主要用於循環處理結果集,有點兒像for循環。那麼爲何使用遊標?使用遊標有什麼做用?在關係型數據庫中,咱們查詢的集合是面向集合的數據庫
而遊標打破了這一規則,對於遊標來講,思惟方式是面向行的。並且在性能上,遊標吃更多的內存,減小可用的併發,佔用帶寬。若是用取錢來作比喻,咱們取1000元錢。SQL結果集方式是一次取1000元,而採用遊標則是10次取,每此取100元。存在即合理,咱們使用遊標是由於,某些情況沒法實現查詢的時候,咱們使用遊標來實現。編程
遊標的生命週期由五部分構成:定義遊標、打開遊標、使用遊標、關閉遊標、釋放遊標。定義遊標像是在定義遊標變量,有遊標類型+遊標變量。設計模式
//定義遊標
DECLARE test
//定義並賦值
DECLARE test=CUST
//打開遊標
OPEN test
//關閉遊標
CLOSE test
//釋放遊標
DEALLOCATE test
下面是使用遊標的過程,給咱們的結論就是儘可能使用while,子查詢,臨時表,函數,表變量等來替代遊標。遊標性能極差。儘可能不要用,用完必定要釋放。瀏覽器
數據庫應用,在許多軟件系統中常常用到,是開發中大型系統不可缺乏的輔助。但若是對數據庫資源沒有很好地管理(如:沒有及時回收數據庫的遊標(ResultSet)、Statement、鏈接 (Connection)等資源),每每會直接致使系統的穩定。這類不穩定因素,不僅僅由數據庫或者系統自己一方引發,只有系統正式使用後,隨着流量、用戶的增長,纔會逐步顯露。在基於Java開發的系統中,JDBC是程序員和數據庫打交道的主要途徑,提供了完備的數據庫操做方法接口。但考慮到規範的適用性,JDBC只提供了最直接的數據庫操做規範,對數據庫資源管理,如:對物理鏈接的管理及緩衝,指望第三方應用服務器(Application Server)的提供。
本文,以JDBC規範爲基礎,介紹相關的數據庫鏈接池機制,並就若是以簡單的方式,實現有效地管理數據庫資源介紹相關實現技術。
2.在linux下,如何監控和查看JVM的內存。
使用命令jmap,linux下特有的一個命令。用於觀察運行時期jvm物理內存的一個佔用狀況。jmap -heap能夠打印出jvm堆內存的使用狀況。
補充,linux中命令top。用於顯示執行中的程序進程。
linux中命令free,用於顯示內存的使用狀況。(free比top只佔用不多的系統資源)查看free命令輸出內容的解釋:total:總計物理內存的大小。used:已使用多大。
linux中的命令kill用來終止一個進程。查看當前進程用ps,查看當前當前路徑用pwd,文件權限修改chmod,查看幫助文檔man,顯示文件內容cat,
-Xms 和 -Xmx 分別表明分配JVM的最小內存和最大內存。
-Xms:minimum memory size for pile and heap
-Xmx:maximum memory size for pile and heap
3.Executor框架和Fork/Join框架,簡述。
java.util.concurrent.Executors類,用於建立和管理線程。當咱們創建ThreadPool和ThreadFactory的時候,用此類。
而後說說Executor框架,Executor框架指jdk中引入的一系列併發庫中與executor相關的功能,其中包括線程池、Executor、Executors、ExecutorService、CompletionService、Future、Callable等。在Executor框架中,使用執行器Executor來管理Thread對象,從而簡化了併發編程。
Fork/join框架是對原來的Executors更進一步,在原來的基礎上增長了並行分治計算中的一種Work-stealing策略,就是指的是。當一個線程正在等待他建立的子線程運行的時候,當前線程若是完成了本身的任務後,就會尋找尚未被運行的任務而且運行他們,這樣就是和Executors這個方式最大的區別,更加有效的使用了線程的資源和功能。因此很是推薦使用Fork/Join框架。
4.在Java併發編程中,解決共享資源問題衝突的時候,能夠考慮使用鎖機制。
對象的鎖,全部對象都自動含有單一的鎖。JVM負責跟蹤對象被加鎖的次數。若是一個對象被解鎖,其計數變爲0。在任務(線程)第一次給對象加鎖的時候,計數變爲1。 每當這個相同的任務(線程)在此對象上得到鎖時,計數會遞增。只有首先得到鎖的任務(線程)才能繼續獲取該對象上的多個鎖。每當任務離開一個synchronized方法,計數遞減,當計數爲0的時候,鎖被徹底釋放,此時別的任務就可使用此資源。
synchronized同步塊,第一種,當使用同步塊時,若是方法下的同步塊都同步到一個對象上的鎖,則全部的任務(線程)只能互斥的進入這些同步塊。Resource1.java演示了三個線程(包括main線程)試圖進入某個類的三個不一樣的方法的同步塊中,雖然這些同步塊處在不一樣的方法中,但因爲是同步到同一個對象(當前對象 synchronized (this)),因此對它們的方法依然是互斥的。第二種,同步到多個對象鎖,Resource1.java演示了三個線程(包括main線程)試圖進入某個類的三個不一樣的方法的同步塊中,這些同步塊處在不一樣的方法中,而且是同步到三個不一樣的對象(synchronized (this),synchronized (syncObject1),synchronized (syncObject2)),因此對它們的方法中的臨界資源訪問是獨立的。
綜上所述,咱們知道:1.在java.util.concurrent.locks包下提供了另外一種方式實現同步訪問,那就是Lock類。既然有了synchronized,爲何還要使用lock呢?若是一個代碼塊被synchronized修飾了,當一個線程獲取了對應的鎖,並執行該代碼塊時,其餘線程便只能一直等待,等待獲取鎖的線程釋放鎖,而這裏獲取鎖的線程釋放鎖只會有兩種狀況:1)獲取鎖的線程執行完了該代碼塊,而後線程釋放對鎖的佔有;2)線程執行發生異常,此時JVM會讓線程自動釋放鎖。那麼若是這個獲取鎖的線程因爲要等待IO或者其餘緣由(好比調用sleep方法)被阻塞了,可是又沒有釋放鎖,其餘線程便只能乾巴巴地等待,試想一下,這多麼影響程序執行效率。所以就須要有一種機制能夠不讓等待的線程一直無期限地等待下去(好比只等待必定的時間或者可以響應中斷),經過Lock就能夠辦到。再舉個例子:當有多個線程讀寫文件時,讀操做和寫操做會發生衝突現象,寫操做和寫操做會發生衝突現象,可是讀操做和讀操做不會發生衝突現象。可是採用synchronized關鍵字來實現同步的話,就會致使一個問題:若是多個線程都只是進行讀操做,因此當一個線程在進行讀操做時,其餘線程只能等待沒法進行讀操做。所以就須要一種機制來使得多個線程都只是進行讀操做時,線程之間不會發生衝突,經過Lock就能夠辦到。另外,經過Lock能夠知道線程有沒有成功獲取到鎖。這個是synchronized沒法辦到的。那麼也就是說,Lock提供了比synchronized更多的功能。可是要注意如下幾點:1)Lock不是Java語言內置的,synchronized是Java語言的關鍵字,所以是內置特性。Lock是一個類,經過這個類能夠實現同步訪問;2)Lock 和synchronized有一點很是大的不一樣,採用 synchronized不須要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完以後,系統會自動讓線程釋放對鎖的佔用;而Lock則必需要用戶去手動釋放鎖,若是沒有主動釋放鎖,就有可能致使出現死鎖現象。
public void interface Lock{ public void lock(); //獲取鎖
public void unlock(); //釋放鎖,發生異常也要由人來手動釋放鎖,故必須寫在finally代碼中,防止死鎖發生。 }
ReentrantLock類是惟一實現了接口Lock的類,它叫作可重入鎖。
ReadWriteLock也是一個接口,在它裏面只定義了兩個方法:
public void interface ReadWirteLock{
Lock readLock();
Lock writeLock();
}
一個用來獲取讀鎖,一個用來獲取寫鎖。也就是說將文件的讀寫操做分開,分紅2個鎖來分配給線程,從而使得多個線程能夠同時進行讀操做。不過要注意的是,若是有一個線程已經佔用了讀鎖,則此時其餘線程若是要申請寫鎖,則申請寫鎖的線程會一直等待釋放讀鎖。
http://www.cnblogs.com/dolphin0520/p/3923167.html
插播語:Lock和synchronized有如下幾點不一樣:1)Lock是一個接口,而synchronized是Java中的關鍵 字,synchronized是內置的語言實現;2)synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而 Lock在發生異常時,若是沒有主動經過unLock()去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖;3)Lock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響 應中斷;4)經過Lock能夠知道有沒有成功獲取鎖,而synchronized卻沒法辦到。5)Lock能夠提升多個線程進行讀操做的效率。在性能上來講,若是競爭資源不激烈,二者的性能是差很少的,而當競爭資源很是激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優於synchronized。因此說,在具體使用時要根據適當狀況選擇。
什麼叫作可重入鎖?可重入性代表了鎖的分配機制:基於線程的分配,而不是基於方法調用的分配。舉個簡單的例子,當一個線程執行到某個synchronized 方法時,好比說method1,而在method1中會調用另一個synchronized方法method2,此時線程沒必要從新去申請鎖,而是能夠直接執行方法method2。
什麼叫作可中斷鎖?可中斷鎖:顧名思義,就是能夠相應中斷的鎖。在Java中,synchronized就不是可中斷鎖,而Lock是可中斷鎖。若是某一線程A正在執行鎖中的代碼,另外一線程B正在等待獲取該鎖,可能因爲等待時間過長,線程B不想等待了,想先處理其餘事情,咱們可讓它中斷本身或者在別的線程中中斷它,這種就是可中斷鎖。在前面演示lockInterruptibly()的用法時已經體現了Lock的可中斷性。
什麼叫作公平鎖?公平鎖即儘可能以請求鎖的順序來獲取鎖。好比同是有多個線程在等待一個鎖,當這個鎖被釋放時,等待時間最久的線程(最早請求的線程)會得到該所,這種就是公平鎖。非公平鎖即沒法保證鎖的獲取是按照請求鎖的順序進行的。這樣就可能致使某個或者一些線程永遠獲取不到鎖。在Java中,synchronized就是非公平鎖,它沒法保證等待的線程獲取鎖的順序。而對於ReentrantLock和ReentrantReadWriteLock,它默認狀況下是非公平鎖,可是能夠設置爲公平鎖。
什麼叫作讀寫鎖?讀寫鎖將對一個資源(好比文件)的訪問分紅了2個鎖,一個讀鎖和一個寫鎖。正由於有了讀寫鎖,才使得多個線程之間的讀操做不會發生衝突。ReadWriteLock就是讀寫鎖,它是一個接口,ReentrantReadWriteLock實現了這個接口。能夠經過readLock()獲取讀鎖,經過writeLock()獲取寫鎖。上面已經演示過了讀寫鎖的使用方法,在此再也不贅述。
基本上全部的併發模式在解決線程安全問題時,都採用「序列化訪問臨界資源」的方案,即在同一時刻,只能有一個線程訪問臨界資源,也稱做同步互斥訪問。
5.關鍵字volatile解析。
因爲使用volatile關鍵字是與Java的內存模型有關的,因此先了解一下Java內存模型。cpu、高速緩存、內存。在cpu和高速緩存之間很是細粒度的。使用關鍵字volatile使得細粒度的依然線程安全。
咱們知道使用sychronized是用來修飾代碼塊的,使用volatile則是用來修飾變量的。。當用volatile修飾了變量,線程在每次使用變量的時候,都會讀取變量修改後的最新值。分析代碼以下:
public class Counter{ public static int count = 0; public static void inc(){ try{ Thread.sleep(1); }catch (InterruptException e){ e.printStack(); } count++; } public static void main(String args[]){ for(int i = 0; i < 1000; i++){ new Thread(new Runnable(){ @override public void run(){ Counter.inc(); } }).start(); } System.out.println("運行結果是:Counter.count:"+Counter.count); } }
實際上代碼的運行結果,每次都不同。那麼咱們可能以爲在變量上加上volatile以後,是否是就不會產生線程不安全的問題了,結果不是咱們所指望的結果了。
那麼咱們把代碼修改成public volatile static int count = 0;這時候再運行一次,看到結果仍然不是咱們所指望的1000,是什麼緣由呢?分析一下:
咱們看上面這個圖。咱們知道當JVM運行的時候,其中有一起內存區域是虛擬機棧,是線程私有的。用於保存線程運行時的變量信息。當線程訪問某一個對象的值的時候,首先經過對象的應用找到對應在堆內存中的變量的值。而後把堆內存變量的具體值load到線程本地內存中,創建一個變量副本,以後線程就再也不和對象在堆內存的變量值有任何關係,而是直接修改副本變量的值。當修改完以後的某一個時刻,線程退出以前,自動把線程變量副本的值會寫到對象在堆中的變量。這樣在堆中的對象的值就產生變化了。
read and load 從堆複製變量到當前工做內存(虛擬機棧)
use and assign 執行代碼,改變共享變量值 其中use and assign 能夠屢次出現
store and write 用工做內存(虛擬機棧)數據刷新堆內存的相關內容
可是這一些操做並非原子性,也就是 在read load以後,若是主內存count變量發生修改以後,線程工做內存中的值因爲已經加載,不會產生對應的變化,因此計算出來的結果會和預期不同。對於volatile修飾的變量,jvm虛擬機只是保證從主內存加載到線程工做內存的值是最新的。例如假如線程1,線程2 在進行read,load 操做中,發現主內存中count的值都是5,那麼都會加載這個最新的值。在線程1堆count進行修改以後,會write到主內存中,主內存中的count變量就會變爲6。線程2因爲已經進行read,load操做,在進行運算以後,也會更新主內存count的變量值爲6。致使兩個線程及時用volatile關鍵字修改以後,仍是會存在併發的狀況。
(兩個進程同時修改x的值,而且同時開始同時完成,則沒法保證數據的指望)。volatile具有sychronized的可見性特性,可是不具有原則特性。
******************************************************************************************************************************************************************
說完多線程在說說面試中經常被問到的Servlet。前面說到了Tomcat是容器,Servlet組件,web項目都是提供的基於Servlet接口或者jar包實現的,至於SpringMVC都是小助手,簡化咱們項目開發的一些催化劑和輔助功能。
第一,Servlet的生命週期:
Servlet 生命週期:Servlet 加載--->實例化--->服務--->銷燬。
init():在Servlet的生命週期中,僅執行一次init()方法。它是在服務器裝入Servlet時執行的,負責初始化Servlet對象。能夠配置服務器,以在啓動服務器或客戶機首次訪問Servlet時裝入Servlet。不管有多少客戶機訪問Servlet,都不會重複執行init()。
service():它是Servlet的核心,負責響應客戶的請求。每當一個客戶請求一個HttpServlet對象,該對象的Service()方法就要調用,並且傳遞給這個方法一個「請求」(ServletReque
st)對象和一個「響應」(ServletResponse)對象做爲參數。在HttpServlet中已存在Service()方法。默認的服務功能是調用與HTTP請求的方法相應的do功能。
destroy(): 僅執行一次,在服務器端中止且卸載Servlet時執行該方法。當Servlet對象退出生命週期時,負責釋放佔用的資源。一個Servlet在運行service()方法時可能會產生其餘的線程,所以須要確認在調用destroy()方法時,這些線程已經終止或完成。
Tomcat 與 Servlet 是如何工做的:
咱們分析一下這個執行步驟:
1.web client(就是瀏覽器)向Servlet容器(就是Tomcat)發出http請求。
2.Servlet容器接受web client請求。
3.Servlet容器建立一個HttpRequest對象,將web client請求的信息封裝到這個對象中。(怪不得經過request.get方法能夠獲取請求信息)
4.Servlet容器建立一個HttpResponse對象。
5.Servlet容器調用HttpServlet對象的service( )方法, 把HttpRequest對象和HttpResponse對象做爲參數傳給HttpServlet對象。
6.HttpServlet調用HttpRequest對象的有關方法,獲取http請求信息。
7.HttpServlet調用HttpResponse對象的有關方法,生成響應數據。
8.Servlet容器把HttpServlet的響應結果傳給Web Client。
第二,說說Servlet的工做原理。
Servlet工做原理,首先簡單解釋一下Servlet接收和響應客戶請求的過程,首先客戶發送一個請求,Servlet是調用service()方法對請求進行響應的,經過源代碼可見,service()方法中對請求的方式進行了匹配,選擇調用doGet,doPost等這些方法,而後再進入對應的方法中調用邏輯層的方法,實現對客戶的響應。在Servlet接口和GenericServlet中是沒有doGet()、doPost()等等這些方法的,HttpServlet中定義了這些方法,可是都是返回error信息,因此,咱們每次定義一個Servlet的時候,都必須實現doGet或doPost等這些方法。其次,每個自定義的Servlet都必須實現Servlet的接口,Servlet接口中定義了五個方法,其中比較重要的三個方法涉及到Servlet的生命週期,分別是上文提到的init(),service(),destroy()方法。GenericServlet是一個通用的,不特定於任何協議的Servlet,它實現了Servlet接口。而HttpServlet繼承於GenericServlet,所以HttpServlet也實現了Servlet接口。因此咱們定義Servlet的時候只須要繼承HttpServlet便可。Servlet接口和GenericServlet是不特定於任何協議的,而HttpServlet是特定於HTTP協議的類,因此HttpServlet中實現了service()方法,並將請求ServletRequest、ServletResponse 強轉爲HttpRequest 和 HttpResponse。
第三,說說建立Servlet對象的時機。
<servlet> <servlet-name>Init</servlet-name> <servlet-class>org.xl.servlet.InitServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
建立Servlet對象的時機:Servlet容器(tomcat)啓動時:讀取web.xml配置文件中的信息,構造指定的Servlet對象,建立ServletConfig對象,同時將ServletConfig對象做爲參數來調用Servlet對象的init方法。在Servlet容器啓動後:客戶首次向Servlet發出請求,Servlet容器會判斷內存中是否存在指定的Servlet對象,若是沒有則建立它,而後根據客戶的請求建立HttpRequest、HttpResponse對象,從而調用Servlet 對象的service方法。Servlet Servlet容器在啓動時自動建立Servlet,這是由在web.xml文件中爲Servlet設置的<load-on-startup>屬性決定的。從中咱們也能看到同一個類型的Servlet對象在Servlet容器中以單例的形式存在。
第四,Servlet是線程安全的麼?
先給你答案:Servlet規範中說Servlet不是線程安全的。接下來依次理解什麼是線程不安全?Servlet爲何線程不安全?如何解決Servlet中的線程不安全問題?
線程安全的概念,場景:多個線程同時在訪問一段代碼,或同時操做某個變量。若是每次運行結果和單線程運行的結果是同樣的,並且其餘變量的值也和預期的是同樣的,就是線程安全的。反之,就是線程不安全的。(其實線程安全的機制並非僅僅是由sychronized修飾,阿里社招犯的錯。)
爲何Servlet線程不安全,咱們前面看到init()方法只執行一次,Servlet容器中只有一個實例化的對象,它是singleton的,因此會產生多個線程同時訪問一個Servlet實例對象。若是咱們在惟一的Servlet實例變量中定義了全局變量和靜態變量。那麼線程不安全是必然的。因此說,Servlet的線程安全完由它的實現來決定的,若是它的內部屬性或方法被多個線程改變,它就是線程不安全的。
解決Servlet的線程不安全問題,有如下幾種方案,咱們先看一個代碼實例:
// 有以下的代碼: public class TestServlet extends HttpServlet { private int count = 0; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("<HTML><BODY>"); response.getWriter().println(this + " ==> "); response.getWriter().println(Thread.currentThread() + ": <br>"); for(int i=0;i<5;i++){ response.getWriter().println("count = " + count + "<BR>"); try { Thread.sleep(1000); count++; } catch (Exception e) { e.printStackTrace(); } } response.getWriter().println("</BODY></HTML>"); } } //當同時打開多個瀏覽器,輸入http://localhost:8080/ServletTest/TestServlet時,他們顯示的結果不一樣,這就說明了對於屬性count來講,它是線程不安全的,將以上的代碼重構,則有
public class TestServlet extends HttpServlet { private int count = 0; private String synchronizeStr = ""; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("<HTML><BODY>"); response.getWriter().println(this + " ==> "); response.getWriter().println(Thread.currentThread() + ": <br>"); synchronized (synchronizeStr){ for(int i=0;i<5;i++){ response.getWriter().println("count = " + count + "<BR>"); try { Thread.sleep(1000); count++; } catch (Exception e) { e.printStackTrace(); } } } response.getWriter().println("</BODY></HTML>"); } }
重構以後的代碼是安全的。
可是,採用了上面的方法以後,同時有100個瀏覽器來訪問的時候,會形成堵塞的問題,排隊會排好長,像上海的浦東12路公交車。改進的方法就是:
沒有用synchronized來同步,由於會形成堵塞;而是採用了實現接口SingleThreadModel
public class TestServlet extends HttpServlet implements SingleThreadModel{
private int count = 0; private String synchronizeStr = ""; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("<HTML><BODY>"); response.getWriter().println(this + " ==> "); response.getWriter().println(Thread.currentThread() + ": <br>"); for(int i=0;i<5;i++){ response.getWriter().println("count = " + count + "<BR>"); try { Thread.sleep(1000); count++; } catch (Exception e) { e.printStackTrace(); } } response.getWriter().println("</BODY></HTML>"); } }
該接口指定了系統如何處理對同一個Servlet的調用,若是一個Servlet被這個接口指定,那麼這個Servlet接口中的service()方法將不會有兩個線程被同時執行,固然也不存在線程安全的問題。
第五,基於以上的理解,咱們知道了容器、組件、Servlet,那麼他們跟咱們的框架SpringMVC又是什麼關係呢?
咱們知道:JSP+Servlet+javabean是最基本的jsp技術。前臺的標籤jstl、freemarker等都是爲了使jsp頁面看起來更簡潔、代碼更容易維護。
*************************************************************************************************************************************************************************
重點知識:Java中的TreeMap和TreeSet,以及紅黑樹和AVL樹。
AVL樹,就是平衡二叉查找樹。特色:左右子樹高度之差的絕對值最多爲1.
紅黑樹,是一種二叉查找樹,它在每一個節點上增長一個存儲位表示節點的顏色。能夠是Red或Black。經過對任何一條從根到葉子節點的路徑上的各個節點着色方式的限制,紅黑樹確保沒有一條路徑會比其餘路徑長處兩倍。於是是接近平衡的。紅黑樹上每一個結點內含五個域,color,key,left,right,p。若是相應的指針域沒有,則設爲NIL。每一個結點要麼是紅的,要麼是黑的。根結點是黑的。每一個葉結點,即空結點(NIL)是黑的。若是一個結點是紅的,那麼它的倆個兒子都是黑的。對每一個結點,從該結點到其子孫結點的全部路徑上包含相同數目的黑結點。
多線程中的數據爭用問題:
共享變量:若是一個變量在多個線程中的工做內存中都存在副本,那麼這個變量就是這幾個線程的共享變量。
可見性:一個線程對共享變量值的修改,可以及時的被其餘線程看到。
JVM內存模型,規範了Java程序中各類變量的訪問規則,只有共享變量纔會引發線程的爭用。(全部的變量都在主內存中,每一個線程都有本身獨立的工做內存,裏面保存着該線程使用到的變量副本)。
線程對共享變量的全部操做都必須在本身的工做內存中進行,不能直接從主內存中讀寫;同時不一樣線程之間沒法訪問其餘線程工做內存中的變量,線程間變量值的傳遞須要經過主內存來完成。共享變量的值的傳遞是須要經過主內存這個橋樑來完成的。若是線程一對共享變量值的修改,想要被線程二及時的看到,必需要通過兩個步驟:把工做內存一中更新過的共享變量刷新到主內存中,而後,將主內存中最新的共享變量值更新到工做內存二中。
要實現共享變量的可見性,必須保證兩點:線程修改後的共享變量值可以及時從工做內存刷新到主內存中。其餘線程可以及時的把共享變量的最新值從主內存更新到本身的工做內存中。Java語言層面支持的可見性實現方式有兩種,Sychronized和volatile。sychronized是同步,它能夠實現互斥鎖,它能保證在同一時刻只能有一個線程在執行鎖裏面的代碼。因此sychronized可以保證鎖內實現的一個原子性。同時sychronized還有另一個特性,內存可見性。JDK中對sychronized有這樣的規定:線程解鎖前,必須把共享變量的最新值刷新到主內存中,線程加鎖時,將清空工做內存中的共享變量的值,從而使用共享變量時,將會從主內存中重新讀取最新值。(注意:加鎖和解鎖須要同一把鎖!!)這樣就保證了線程對共享變量的修改在下次加鎖時對其餘線程的可見。綜上所述,sychronized實現可見性的工做步驟以下:1.首先會得到互斥鎖,2.而後清空工做內存,3.而後從主內存拷貝變量的最新副本到工做內存,4.接着執行代碼,5.把更改後的共享變量值再刷新到主內存中,6.釋放互斥鎖。
接下來,咱們再看看關鍵字volatile,它能保證可見性,可是不能保證原子性。接下來分析一下爲何volatile不是原子性。如private int number = 0; number++;很顯然,這個number++不是原子操做,由於它分爲三個步驟,讀取number的值,將number的值加1,寫入最新的number的值。那麼sychronized{number++};很顯然能夠保證了原子性。若是採用private volatile int number = 0;它是不能保證原子性的。
Java的IO輸入輸出流,包括幾點:字節流和字符流的使用;編碼問題;File類的使用;RandomAccessFile的使用;對象的序列化和反序列化。