1. 程式人生 > >面試攻略一(JAVA基礎專題)

面試攻略一(JAVA基礎專題)

 

同步非同步阻塞非阻塞

同步與非同步

  實際上同步與非同步是針對應用程式與核心的互動而言的。同步過程中程序觸發IO操作並等待或者輪詢的去檢視IO操作是否完成。非同步過程中程序觸發IO操作以後,直接返回,做自己的事情,IO交給核心來處理,完成後核心通知程序IO完成。同步與非同步如下圖所示:

阻塞與非阻塞

  簡單理解為需要做一件事能不能立即得到返回應答,如果不能立即獲得返回,需要等待,那就阻塞了,否則就可以理解為非阻塞。

什麼是Servlet

Java Servlet 是執行在 Web 伺服器或應用伺服器上的程式,它是作為來自 Web 瀏覽器或其他 HTTP 客戶端的請求和 HTTP 伺服器上的資料庫或應用程式之間的中間層。

重點理解:Servlet 編寫過濾器

Servlet 和 Filter 的比較

  • servlet:servlet是一種執行伺服器端的java應用程式,具有獨立於平臺和協議的特性,並且可以動態的生成web頁面,它工作在客戶端請求與伺服器響應的中間層。
  • filter:filter是一個可以複用的程式碼片段,可以用來轉換HTTP請求、響應和頭資訊。Filter不像Servlet,它不能產生一個請求或者響應,它只是修改對某一資源的請求,或者修改從某一的響應。

詳解:filter與servlet的比較(概念 、 生命週期 、職責 、執行過程)

什麼是中介軟體

中介軟體是一類獨立的系統軟體或服務程式,分散式應用軟體藉助這種軟體在不同的技術之間共享資源。中介軟體位於客戶機/伺服器的作業系統之上,管理計算機資源和網路通訊。

常見的有如下幾種:服務中介軟體、整合中介軟體、資料中介軟體、訊息中介軟體、安全中介軟體。

用Java實現的中介軟體,統稱Java中介軟體。中介軟體,可以理解為類庫,介於類庫和產品之間。

說簡單一點,就是你的程式A和程式B互相通訊使用的協議,程式A,B可以由不同語言不同平臺構建。但是協議可以保證他們能互相認識互發的東西。

舉例:
1 , RMI ( Remote Method Invocations, 遠端呼叫)
2 , Load Balancing( 負載均衡,將訪問負荷分散到各個伺服器中 )
3 , Transparent Fail-over( 透明的故障切換 )
4 , Clustering( 叢集 , 用多個小的伺服器代替大型機)
5 , Back-end-Integration( 後端整合,用現有的、新開發的系統如何去整合遺留的系統 )
6 , T ransaction 事務(全域性 / 區域性)全域性事務(分散式事務)區域性事務(在同一資料庫聯 接
內的事務)
7 , Dynamic Redeployment ( 動態重新部署 , 在不停止原系統的情況下,部署新的系統)
8 , System Management( 系統管理 )
9 , Threading( 多執行緒處理 )
10 , Message-oriented Middleware 面向訊息的中介軟體(非同步的呼叫程式設計)
11 , Component Life Cycle( 元件的生命週期管理 )
12 , Resource pooling (資源池)
13 , Security (安全)
14 , Caching (快取)

什麼是反射

定義:

用一句話來解釋反射的定義:自控制,自描述。即通過反射可以動態的獲取類、屬性、方法的資訊,也能構造物件並控制物件的屬性和行為。

應用:

JDBC的驅動載入方式是通過反射機制實現的,JDBC只是設計了驅動需要實現的介面,並不關心驅動廠商的個數和實現方式,只要安裝統一的規範即可,至於型別的判斷和具體方法的觸發,交給執行期動態判斷即可,這種反射機制的使用淋漓盡致的體現了多型,並且降低了類與類之間的耦合度。

什麼是面向物件

OOP , Object-Oriented Programming ,面向物件程式設計不同於面向過程程式設計:
1 ) OOP 關注物件和角色,也就是事物的本質
2) OOP 把客觀世界中的物件抽象成對應的類;
3 )通過類構造例項;
4)通過依賴、繼承、實現等形式建立物件間的通訊關係

優點:
( 2 ) OOP 易於擴充套件,增加或改變業務的功能,無需大幅改動改變原始碼
( 3 ) OOP 易於建模, OOP 就是軟體架構師在計算機高階語言中對客觀世界的抽象和
再現,

執行緒與執行緒池

附:JAVA多執行緒程式設計

POJO

簡單的Java物件(Plain Ordinary Java Objects)實際就是普通JavaBeans

 POJO有一些private的引數作為物件的屬性。然後針對每個引數定義了get和set方法作為訪問的介面。例如:

public class User {
  private long id;
  private String name;
  public void setId(long id) {
  this.id = id;
  }
  public void setName(String name) {
  this.name=name;
  }
  public long getId() {
  return id;
  }
  public String getName() {
  return name;
  }
  }

POJO物件有時也被稱為Data物件,大量應用於表現現實中的物件。

簡述cookies和session的區別

  1. cookies:是針對每一個網站的資訊,每一個網站只對應一個,其它網站不能訪問,這個檔案是儲存在客戶端的,每次你打相應網站,瀏覽器會查詢這個網站的cookies,如果有就會將這個檔案起傳送出去。cookies檔案的內容大致包函這些資訊如使用者名稱,密碼,設定等,也可以是web伺服器按照一定演算法產生的只有Web伺服器可以理解的資料,這些資料傳送給客戶端,客戶端帶著這些資料訪問該網站才能被該網站識別。
  2. 從session: 是針對每一個使用者的,只有客戶機訪問,程式就會為這個客戶新增一個session。session裡主要儲存的是使用者的登入資訊,操作資訊等。這個session在使用者訪問結束後會被自動消失(如果超時也會)。

簡述Servlet與JSP的關係

        最重要的一點就是 JSP就是servlet ,  jsp繼承了servlet,JSP與Servlet主要有兩方面的不同:編譯:JSP修改後可以立即看到結果,不需要編譯;而Servelt缺需要編譯。轉換:JSP是動態網頁開發技術,是執行在伺服器端的指令碼語言,而Servlet是web伺服器端程式設計技術。所以JSP執行時就是轉換為Servlet,也就是java程式來執行。

作用範圍:application > session > request > page

application所有使用者,專案的所有頁面
session:專案的所有頁面(僅限於使用者的一次登入) 
request:僅限於使用者的一次登入,只適用於一次請求,資料只能兩個頁面傳輸
page資料只在本頁面可用

JSP內建物件有哪些,各自起到的作用

1、HttpServletRequest的 request物件
作用:代表請求物件,用來接收客戶端通過http協議連線傳輸到伺服器端的資料。

2、HttpServletResponse的response物件
作用:代表響應物件,用來向客戶端傳送資料。

3、JspWriter的 out 物件
作用:主要用於向客戶端傳送資料。其中JspWriter是out的基類。

4、HttpSession 的session 物件
作用:主要用於來分別儲存每個使用者的個人資訊,與請求關聯的對話。會話狀態的維持是每個web應用開發者都必須面對的問題。

5、ServletContext的application物件
作用:主要用於儲存使用者資訊,程式碼片斷的執行環境。它是一個共享的內建物件。即一個容器中多個使用者共享一個application物件,故其儲存的資訊被所有的使用者所共享。

6、PageContext的PageContext物件
作用:管理網頁的屬性,為jsp頁面包裝頁面的上下文,管理對屬於jsp中特殊可見部分中已經命名物件的訪問。它的建立和初始化的工作都是由容器來自動完成的。

7、ServletConfig的Config物件
作用:程式碼片斷配置物件,表示對servlet的配置。

8、Object 的page(相當於this) 物件
作用:處理jsp網頁,是object類的一個例項。即它也是jsp的本身,只有在jsp的頁面範圍之內它才是合法的。

9、Exception
作用:處理jsp頁面執行時,發生的錯誤和異常。

常用的設計模式(虛擬碼),並簡述應用場景

七大設計原則:

1、單一職責原則【SINGLE RESPONSIBILITY PRINCIPLE】:一個類負責一項職責.
2、里氏替換原則【LISKOV SUBSTITUTION PRINCIPLE】:繼承與派生的規則.
3、依賴倒置原則【DEPENDENCE INVERSION PRINCIPLE】:高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。即針對介面程式設計,不要針對實現程式設計.
4、介面隔離原則【INTERFACE SEGREGATION PRINCIPLE】:建立單一介面,不要建立龐大臃腫的介面,儘量細化介面,介面中的方法儘量少.
5、迪米特法則【LOW OF DEMETER】:低耦合,高內聚.
6、開閉原則【OPEN CLOSE PRINCIPLE】:一個軟體實體如類、模組和函式應該對擴充套件開放,對修改關閉.
7、組合/聚合複用原則【Composition/Aggregation Reuse Principle(CARP) 】:儘量使用組合和聚合少使用繼承的關係來達到複用的原則.

附:Java23種設計模式

附:Java23種設計模式(經典)

HashMap實現原理及原始碼分析

HashMap實現原理及原始碼分析

HashMap和HashTable區別

HashTable

  • 底層陣列+連結串列實現,無論key還是value都不能為null,執行緒安全,實現執行緒安全的方式是在修改資料時鎖住整個HashTable,效率低,ConcurrentHashMap做了相關優化
  • 初始size為11,擴容:newsize = olesize*2+1
  • 計算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

HashMap

  • 底層陣列+連結串列實現,可以儲存null鍵和null值,執行緒不安全
  • 初始size為16,擴容:newsize = oldsize*2,size一定為2的n次冪
  • 擴容針對整個Map,每次擴容時,原來陣列中的元素依次重新計算存放位置,並重新插入
  • 插入元素後才判斷該不該擴容,有可能無效擴容(插入後如果擴容,如果沒有再次插入,就會產生無效擴容)
  • 當Map中元素總數超過Entry陣列的75%,觸發擴容操作,為了減少連結串列長度,元素分配更均勻
  • 計算index方法:index = hash & (tab.length – 1)

HashSet和TreeSet的區別

HashSet
HashSet有以下特點
 不能保證元素的排列順序,順序有可能發生變化
 不是同步的
 集合元素可以是null,但只能放入一個null
當向HashSet集合中存入一個元素時,HashSet會呼叫該物件的hashCode()方法來得到該物件的hashCode值,然後根據 hashCode值來決定該物件在HashSet中儲存位置。
簡單的說,HashSet集合判斷兩個元素相等的標準是兩個物件通過equals方法比較相等,並且兩個物件的hashCode()方法返回值相 等
注意,如果要把一個物件放入HashSet中,重寫該物件對應類的equals方法,也應該重寫其hashCode()方法。其規則是如果兩個對 象通過equals方法比較返回true時,其hashCode也應該相同。另外,物件中用作equals比較標準的屬性,都應該用來計算 hashCode的值。

TreeSet類
TreeSet是SortedSet介面的唯一實現類,TreeSet可以確保集合元素處於排序狀態。TreeSet支援兩種排序方式,自然排序 和定製排序,其中自然排序為預設的排序方式。向TreeSet中加入的應該是同一個類的物件。
TreeSet判斷兩個物件不相等的方式是兩個物件通過equals方法返回false,或者通過CompareTo方法比較沒有返回0
自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法來比較元素之間大小關係,然後將元素按照升序排列。
Java提供了一個Comparable介面,該接口裡定義了一個compareTo(Object obj)方法,該方法返回一個整數值,實現了該介面的物件就可以比較大小。
obj1.compareTo(obj2)方法如果返回0,則說明被比較的兩個物件相等,如果返回一個正數,則表明obj1大於obj2,如果是 負數,則表明obj1小於obj2。
如果我們將兩個物件的equals方法總是返回true,則這兩個物件的compareTo方法返回應該返回0
定製排序
自然排序是根據集合元素的大小,以升序排列,如果要定製排序,應該使用Comparator介面,實現 int compare(T o1,T o2)方法。

最重要:

1、TreeSet 是二差樹實現的,Treeset中的資料是自動排好序的,不允許放入null值。 

2、HashSet 是雜湊表實現的,HashSet中的資料是無序的,可以放入null,但只能放入一個null,兩者中的值都不能重複,就如資料庫中唯一約束。 

3、HashSet要求放入的物件必須實現HashCode()方法,放入的物件,是以hashcode碼作為標識的,而具有相同內容的 String物件,hashcode是一樣,所以放入的內容不能重複。但是同一個類的物件可以放入不同的例項 。

HashSet與HashMap的區別:

HashMap HashSet
實現了Map介面 實現Set介面
儲存鍵值對 僅儲存物件
呼叫put()向map中新增元素 呼叫add()方法向Set中新增元素
HashMap使用鍵(Key)計算Hashcode

HashSet使用成員物件來計算hashcode值,

對於兩個物件來說hashcode可能相同,

所以equals()方法用來判斷物件的相等性,

如果兩個物件不同的話,那麼返回false

HashMap相對於HashSet較快,因為它是使用唯一的鍵獲取物件 HashSet較HashMap來說比較慢
 

 

JAVA集合框架

簡述泛型、反射、註解應用場景及各自解決了哪些問題

泛型:

  型別泛化,即由具體的、個別的擴大為一般的。就是通過型別引數化,來解決程式的通用性設計和實現的若干問題。比如:

  • 編譯器型別檢查問題
  • 強制型別轉換問題
  • 可讀性,靈活性問題

泛型萬用字元約定:

<? extends T> 上界萬用字元

上界萬用字元顧名思義,<? extends T>表示的是型別的上界(包含自身),因此通配的引數化型別可能是T或T的子類。正因為無法確定具體的型別是什麼,add方法受限(可以新增null,因為null表示任何型別),但可以從列表中獲取元素後賦值給父型別。如上圖中的第一個例子,第三個add()操作會受限,原因在於List<Animal>和List<Cat>是List<? extends Animal>的子型別。

 

<? super T> 下界萬用字元

下界萬用字元<? super T>表示的是引數化型別是T的超型別(包含自身),層層至上,直至Object,編譯器無從判斷get()返回的物件的型別是什麼,因此get()方法受限。但是可以進行add()方法,add()方法可以新增T型別和T型別的子型別,如第二個例子中首先添加了一個Cat型別物件,然後添加了兩個Cat子類型別的物件,這種方法是可行的,但是如果新增一個Animal型別的物件,顯然將繼承的關係弄反了,是不可行的。

 

<?> 無界萬用字元

在理解了上界萬用字元和下界萬用字元之後,其實也自然而然的理解了無界萬用字元。無界萬用字元用<?>表示,?代表了任何的一種型別,能代表任何一種型別的只有null(Object本身也算是一種型別,但卻不能代表任何一種型別,所以List<Object>和List<null>的含義是不同的,前者型別是Object,也就是繼承樹的最上層,而後者的型別完全是未知的)。

 

反射

定義:

用一句話來解釋反射的定義:自控制,自描述。即通過反射可以動態的獲取類、屬性、方法的資訊,也能構造物件並控制物件的屬性和行為。

應用:

JDBC的驅動載入方式是通過反射機制實現的,JDBC只是設計了驅動需要實現的介面,並不關心驅動廠商的個數和實現方式,只要安裝統一的規範即可,至於型別的判斷和具體方法的觸發,交給執行期動態判斷即可,這種反射機制的使用淋漓盡致的體現了多型,並且降低了類與類之間的耦合度。

 

註解

定義:可以把註解理解為程式碼裡的特殊標記,這些標記可以在編譯,類載入,執行時被讀取,並執行相應的處理。

應用:@Override   @controller  

 

Thread類的方法有哪些,如何多種方式實現執行緒同步

多執行緒有兩種實現方法,一是繼承Thread類,重寫方法run(),二是實現Runnable介面,實現方法run();

同步有兩種實現方法,分別是synchronized、wait與notify。

程序與執行緒的區別,JAVA中有哪些方式可以建立執行緒。

參考:java中程序與執行緒--三種實現方式

程序:系統分配資源的基本單位,資源獨立

執行緒:系統(CPU)排程的基本單位,也是程式執行的最小單位。與同進程中的其他執行緒共享資料,但擁有自己的棧空間,擁有獨立的執行序列

關係:一個執行緒可以建立和撤銷另一個執行緒;同一個程序中的多個執行緒之間可以併發執行.簡而言之,一個程式至少有一個程序,一個程序至少有一個執行緒.

建立方式:

  • 繼承自Thread類並重寫 run() 方法,
  • 實現 Runnable 介面並重寫 run() 方法
  • 使用callable和future建立執行緒

程序的五種基本狀態

è¿éåå¾çæè¿°

  • 建立狀態:程序在建立時需要申請一個空白PCB,向其中填寫控制和管理程序的資訊,完成資源分配。如果建立工作無法完成,比如資源無法滿足,就無法被排程執行,把此時程序所處狀態稱為建立狀態
  • 就緒狀態:程序已經準備好,已分配到所需資源,只要分配到CPU就能夠立即執行
  • 執行狀態:程序處於就緒狀態被排程後,程序進入執行狀態
  • 阻塞狀態:正在執行的程序由於某些事件(I/O請求,申請快取區失敗)而暫時無法執行,程序受到阻塞。在滿足請求時進入就緒狀態等待系統呼叫
  • 終止狀態:程序結束,或出現錯誤,或被系統終止,進入終止狀態。無法再執行

現有一學生表結構(student_id,class_id,name),請寫出統計每班有多少學生的SQL語句

select class_id,count(*) from student group by class_id

假如你正在開發一個系統的登入程式,請簡述你是如何實現記住使用者名稱和密碼這個操作的,並如何實現?

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8"%>
  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  <html>
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Insert title here</title>
  
  <%
     String username = "";
     String password = "";
     //獲取當前站點的所有Cookie
     Cookie[] cookies = request.getCookies();
     for (int i = 0; i < cookies.length; i++) {//對cookies中的資料進行遍歷,找到使用者名稱、密碼的資料
         if ("username".equals(cookies[i].getName())) {
             username = cookies[i].getValue();
         } else if ("password".equals(cookies[i].getName())) {
             password = cookies[i].getValue();
         }
     }
 %>
 
 </head>
 <body>
     <form action="login_handler.jsp" method="post">
         username:<input type="text" name="name" value="<%=username%>" /><br/>
         password:<input type="password" name="pwd" value="<%=password%>" /><br/>
         <input type="checkbox" value="y" name="isLogin">自動登入<br/> 
         <input type="submit" value="登入" />
     </form>
 </body>
 </html>

controller:

@RequestMapping(value = "login", method = RequestMethod.POST)
	public String login(String firstname, String lastname,ModelMap model,HttpServletRequest request,HttpServletResponse response) {
		User user=userService.findByUsername(firstname);
		if(user!=null && lastname!=null){
			if(lastname.equals(user.getPassword())){
				model.put("username", firstname);
			    HttpSession session = request.getSession(); 
				session.setAttribute("users",user);
				model.put("nowtime", new Date());
				model.put("users", user);
				String flag=request.getParameter("remember");
				if("1".equals(flag)){
					//建立兩個cookie物件
					Cookie namecookie=new Cookie("username",firstname);
					//設定cookie的有效天為一週
					namecookie.setMaxAge(60*60*24*7);
					Cookie passcookie=new Cookie("password",lastname);
					passcookie.setMaxAge(60*60*24*7);
					response.addCookie(namecookie);
					response.addCookie(passcookie);
				}
				return "redirect:/ toList.do";
			}
		}else{
			model.put("error", "賬號或密碼錯誤");
			return "manager/login";
		}
		return "manager/login";
	}

在檢視層不支援儲存cookie,服務端不支援session的場景下如何保持使用者登陸狀態。

檢視層生成併發送sessionID(檢視層的每一次瀏覽器請求都攜帶此sessionID),服務端接收sessionID並據此查詢使用者登入資訊

黑盒測試、灰盒測試、白盒測試、單元測試有什麼區別?

黑盒測試關注程式的功能是否正確,面向實際使用者;

白盒測試關注程式原始碼的內部邏輯結構是否正確,面向程式設計人員;

灰盒測試是介於白盒測試與黑盒測試之間的一種測試。

單元測試(Unit Testing)是對軟體基本組成單元進行的測試,如函式或是一個類的方法。這裡的單元,就是軟體設計的最小單位。

怎麼對資料庫百萬級資料進行優化?

使用讀寫分離技術(

主資料庫(master)處理事務性增、改、刪操作(INSERT、UPDATE、DELETE),
從資料庫(slave)處理SELECT查詢操作

springmvc生命週期

  1. 客戶端發出http請求,只要請求形式符合web.xml檔案中配置的*.action的話,就由DispatcherServlet來處理。
  2. DispatcherServlet再將http請求委託給對映器的物件來將http請求交給對應的Action來處理
  3. 對映器根據客戶的http請求,再對比<bean name="/hello.action如果匹配正確,再將http請求交給程式設計師寫的Action
  4. 執行Action中的業務方法,最終返回一個名叫ModelAndView的物件,其中封裝了向檢視傳送的資料和檢視的邏輯名
  5. ModelAndView物件隨著響應到到DispatcherServlet中了
  6. 這時DispatcherServlet收到了ModelAndView物件,它也不知道檢視邏輯名是何意,又得委託一個名叫檢視解析器的物件去具體解析ModelAndView物件中的內容
  7. 將檢視解析器解析後的內容,再次交由DispatcherServlet核心控制器,這時核心控制器再將請求轉發到具體的檢視頁面,取出資料,再顯示給使用者

servlet生命週期

  • Servlet 通過呼叫 init () 方法進行初始化。
  • Servlet 呼叫 service() 方法來處理客戶端的請求。
  • Servlet 通過呼叫 destroy() 方法終止(結束)。
  • 最後,Servlet 是由 JVM 的垃圾回收器進行垃圾回收的

ajax怎麼解決跨域?

  • 代理
  • JSONP
  • 新增響應頭

       addHeader(‘Access-Control-Allow-Origin:*’);//允許所有來源訪問 
  addHeader(‘Access-Control-Allow-Method:POST,GET’);//允許訪問的方式

Mysql索引型別

  • 普通索引
  • 唯一索引
  • 主鍵索引
  • 組合索引
  • 全文索引

參考:Mysql索引

SpringCloud是什麼

Spring Cloud 是致力於分散式系統、雲服務的框架。

為開發人員提供了快速構建分散式系統中一些常見模式的工具,例如:

  • 配置管理
  • 服務註冊與發現
  • 斷路器
  • 智慧路由
  • 服務間呼叫
  • 負載均衡
  • 微代理
  • 控制匯流排
  • 一次性令牌
  • 全域性鎖
  • 領導選舉
  • 分散式會話
  • 叢集狀態
  • 分散式訊息
  • ……

參考:史上最全的 Spring Cloud 教程

Hibernate的三種狀態是什麼?怎麼將遊離狀態轉換為持久化狀態?

對Java執行緒安全與不安全的理解

定義:

存在成員變數的類用於多執行緒時是不安全的,不安全體現在這個成員變數可能發生非原子性的操作,而變數定義在方法內也就是區域性變數是執行緒安全的。想想在使用struts1時,不推薦建立成員變數,因為action是單例的,如果建立了成員變數,就會存線上程不安全的隱患,而struts2是每一次請求都會建立一個action,就不用考慮執行緒安全的問題。所以,日常開發中,通常需要考慮成員變數或者說全域性變數在多執行緒環境下,是否會引發一些問題

經典案例:

多個執行緒執行時,CPU對執行緒的排程是隨機的,我們不知道當前程式被執行到哪步就切換到了下一個執行緒,一個最經典的例子就是銀行匯款問題,一個銀行賬戶存款100,這時一個人從該賬戶取10元,同時另一個人向該賬戶匯10元,那麼餘額應該還是100。那麼此時可能發生這種情況,A執行緒負責取款,B執行緒負責匯款,A從主記憶體讀到100,B從主記憶體讀到100,A執行減10操作,並將資料重新整理到主記憶體,這時主記憶體資料100-10=90,而B記憶體執行加10操作,並將資料重新整理到主記憶體,最後主記憶體資料100+10=110,顯然這是一個嚴重的問題,我們要保證A執行緒和B執行緒有序執行,先取款後匯款或者先匯款後取款,此為有序性。

對原子性的理解:

我們可以把左邊的圓看成是一行程式碼,右邊的圓被分割成了兩行程式碼。如果資料沒有破壞原子性,由於執行緒被排程一次的最少要執行1行程式碼,那麼t1只要執行了這行程式碼,就會連讀帶寫全部完成,其他執行緒再拿到的資料就是被寫過的最新資料,不會有任何安全隱患;而如果資料破壞了原子性,將讀寫進行了分割,那麼t1,讀取完資料如果停掉的話,t2執行的時候拿到的就是一個老資料(沒有被更新的資料),接下來t1,t2同時對相同的老資料進行更新勢必會因此資料的異常。

注意:

  1. 只存在讀資料的時候,不會產生執行緒安全問題。
  2. 在java中,只有同時操作成員(全域性)變數的時候才會產生執行緒安全問題,區域性變數不會(每個執行緒執行時將會把區域性變數放在各自棧幀的工作記憶體中,執行緒間不共享,故不存線上程安全問題,

解決方法:

  1. 使用synchronized解決執行緒安全問題
  • 同步程式碼塊
  • 同步函式
  • 靜態同步函式

可參考:執行緒安全問題

關於鎖

  • 樂觀鎖:

顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量,像資料庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。

  • 悲觀鎖

顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。

  • 兩種鎖各有優缺點,不可認為一種好於另一種,像樂觀鎖適用於寫比較少的情況下,即衝突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生衝突,上層應用會不斷的進行retry,這樣反倒是降低了效能,所以這種情況下用悲觀鎖就比較合適(比如Synchronized就是悲觀鎖)。

死鎖及其解決方案(避免、預防、檢測)

所謂死鎖:是指兩個或兩個以上的程序在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖

 

死鎖產生的原因?

1.因競爭資源發生死鎖 現象:系統中供多個程序共享的資源的數目不足以滿足全部程序的需要時,就會引起對諸資源的競爭而發生死鎖現象

2.程序推進順序不當發生死鎖

死鎖的四個必要條件:

 

(1)互斥條件:程序對所分配到的資源不允許其他程序進行訪問,若其他程序訪問該資源,只能等待,直至佔有該資源的程序使用完成後釋放該資源

(2)請求和保持條件:程序獲得一定的資源之後,又對其他資源發出請求,但是該資源可能被其他程序佔有,此事請求阻塞,但又對自己獲得的資源保持不放

(3)不可剝奪條件:是指程序已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完後自己釋放

(4)環路等待條件:是指程序發生死鎖後,必然存在一個程序--資源之間的環形鏈

 

處理死鎖的基本方法

預防死鎖(破壞四個必要條件)

  • 資源一次性分配:(破壞請求和保持條件)
  • 可剝奪資源:即當某程序新的資源未滿足時,釋放已佔有的資源(破壞不可剝奪條件)
  • 資源有序分配法:系統給每類資源賦予一個編號,每一個程序按編號遞增的順序請求資源,釋放則相反(破壞環路等待條件)

 

避免死鎖(銀行家演算法):

預防死鎖的幾種策略,會嚴重地損害系統性能。因此在避免死鎖時,要施加較弱的限制,從而獲得 較滿意的系統性能。由於在避免死鎖的策略中,允許程序動態地申請資源。因而,系統在進行資源分配之前預先計算資源分配的安全性。若此次分配不會導致系統進入不安全狀態,則將資源分配給程序;否則,程序等待。其中最具有代表性的避免死鎖演算法是銀行家演算法

 

檢測死鎖

  • 首先為每個程序和每個資源指定一個唯一的號碼;
  • 然後建立資源分配表和程序等待表,
  • 通過系統所設定的檢測機構,及時地檢測出死鎖的發生,並精確地確定與死鎖有關的程序和資源,然後採取適當措施,從系統中將已發生的死鎖清除掉。

 

解除死鎖:

當發現有程序死鎖後,便應立即把它從死鎖狀態中解脫出來,常採用的方法有:

  • 剝奪資源:從其它程序剝奪足夠數量的資源給死鎖程序,以解除死鎖狀態;
  • 撤消程序:可以直接撤消死鎖程序或撤消代價最小的程序,直至有足夠的資源可用,死鎖狀態.消除為止;所謂代價是指優先順序、執行代價、程序的重要性和價值等。

 


另外可補充:這是我見過最有用的java面試題,面試了無數公司總結的