度小滿面試題20190923


1. 一面

1. SpringBoot 注解以及自動配置(生效條件)

@SpringBootApplication

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan

@Configuration

1、@SpringBootApplication
  @SpringBootApplication注解是Spring Boot的核心注解,它其實是一個組合注解.
  
  /** @SpringBootApplication注解的源碼

@Target(ElementType.TYPE)注解的作用目標
@Retention(RetentionPolicy.RUNTIME) 注解的保留位置
@Documented 注解將被包含在javadoc中
@Inherited 子類可以繼承父類中的該注解
@SpringBootConfiguration 修飾Spring Boot的配置
@EnableAutoConfiguration 開啟自動配置
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))

該注解會自動掃描包路徑下面的所有@Controller、@Service、@Repository、@Component 的類,不配置包路徑的話,在Spring Boot中默認掃描@SpringBootApplication所在類的同級目錄以及子目錄下的相關注解。

**/

  用在 Spring Boot 主類上,標識這是一個 Spring Boot 應用,用來開啟 Spring Boot 的各項能力。
  這個注解就是 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 這三個注解的組合,也可以用這三個注解來代替 @SpringBootApplication注解。

2、@EnableAutoConfiguration

  允許 Spring Boot 自動配置注解,開啟這個注解之后,Spring Boot 就能根據當前類路徑下的包或者類來配置 Spring Bean。
  如:當前類路徑下有 Mybatis 這個 JAR 包,MybatisAutoConfiguration 注解就能根據相關參數來配置 Mybatis 的各個 Spring Bean。

3、@Configuration

  這是 Spring 3.0 添加的一個注解,用來代替 applicationContext.xml 配置文件,所有這個配置文件里面能做到的事情都可以通過這個注解所在類來進行注冊。

4、@SpringBootConfiguration

  這個注解就是 @Configuration 注解的變體,只是用來修飾是 Spring Boot 配置而已,或者可利於 Spring Boot 后續的擴展。

5、@ComponentScan

  這是 Spring 3.1 添加的一個注解,用來代替配置文件中的 component-scan 配置,開啟組件掃描,即自動掃描包路徑下的 @Component 注解進行注冊 bean 實例到 context 中。6、@Conditional

  這是 Spring 4.0 添加的新注解,用來標識一個 Spring Bean 或者 Configuration 配置文件,當滿足指定的條件才開啟配置7、@ConditionalOnBean

  組合 @Conditional 注解,當容器中有指定的 Bean 才開啟配置。

8、@ConditionalOnMissingBean

  組合 @Conditional 注解,和 @ConditionalOnBean 注解相反,當容器中沒有指定的 Bean 才開啟配置。

9、@ConditionalOnClass

  組合 @Conditional 注解,當容器中有指定的 Class 才開啟配置。

10、@ConditionalOnMissingClass

  組合 @Conditional 注解,和 @ConditionalOnMissingClass 注解相反,當容器中沒有指定的 Class 才開啟配置。

11、@ConditionalOnWebApplication

  組合 @Conditional 注解,當前項目類型是 WEB 項目才開啟配置。
  enum Type {
      /**
       * Any web application will match.
       */
      ANY,
      /**
       * Only servlet-based web application will match.
       */
      SERVLET,
      /**
       * Only reactive-based web application will match.
       */
      REACTIVE
  }
12、@ConditionalOnNotWebApplication

  組合 @Conditional 注解,和 @ConditionalOnWebApplication 注解相反,當前項目類型不是 WEB 項目才開啟配置。

13、@ConditionalOnProperty

  組合 @Conditional 注解,當指定的屬性有指定的值時才開啟配置。

14、@ConditionalOnExpression

  組合 @Conditional 注解,當 SpEL 表達式為 true 時才開啟配置。

15、@ConditionalOnJava

  組合 @Conditional 注解,當運行的 Java JVM 在指定的版本范圍時才開啟配置。

16、@ConditionalOnResource

  組合 @Conditional 注解,當類路徑下有指定的資源才開啟配置。

17、@ConditionalOnJndi

  組合 @Conditional 注解,當指定的 JNDI 存在時才開啟配置。

18、@ConditionalOnCloudPlatform

  組合 @Conditional 注解,當指定的雲平台激活時才開啟配置。

19、@ConditionalOnSingleCandidate

  組合 @Conditional 注解,當指定的 class 在容器中只有一個 Bean,或者同時有多個但為首選時才開啟配置。

20、@ConfigurationProperties

  用來加載額外的配置(如 .properties 文件),可用在 @Configuration 注解類,或者 @Bean注解方法上面。

21、@EnableConfigurationProperties

  一般要配合 @ConfigurationProperties 注解使用,用來開啟對 @ConfigurationProperties注解配置 Bean 的支持。

22、@AutoConfigureAfter

  用在自動配置類上面,表示該自動配置類需要在另外指定的自動配置類配置完之后。

  如 Mybatis 的自動配置類,需要在數據源自動配置類之后。

  @AutoConfigureAfter(DataSourceAutoConfiguration.class)
  public class MybatisAutoConfiguration {}
23、@AutoConfigureBefore   這個和 @AutoConfigureAfter 注解使用相反,表示該自動配置類需要在另外指定的自動配置類配置之前。 24、@Import   這是 Spring 3.0 添加的新注解,用來導入一個或者多個 @Configuration 注解修飾的類,這在 Spring Boot 里面應用很多。 25、@ImportResource   這是 Spring 3.0 添加的新注解,用來導入一個或者多個 Spring 配置文件,這對 Spring Boot 兼容老項目非常有用,因為有些配置無法通過 Java Config 的形式來配置就只能用這個注解來導入。

 

2. SpringMVC過濾器&攔截器

(1)過濾器(Filter):它依賴於servlet容器。
  在實現上,基於函數回調
  它可以對幾乎所有請求進行過濾,但是缺點是一個過濾器實例只能在容器初始化時調用一次。
  使用過濾器的目的,是用來做一些過濾操作,獲取我們想要獲取的數據,
  比如:在Javaweb中,對傳入的request、response提前過濾掉一些信息,或者提前設置一些參數,然后再傳入servlet或者Controller進行業務邏輯操作。
  通常用的場景是:在過濾器中修改字符編碼(CharacterEncodingFilter)、在過濾器中修改HttpServletRequest的一些參數(XSSFilter(自定義過濾器)),如:過濾低俗文字、危險字符等。   web.xml
<filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <servlet-name>/*</servlet-name> </filter-mapping> (2)攔截器(Interceptor):它依賴於web框架,在SpringMVC中就是依賴於SpringMVC框架。
  在實現上,基於Java的反射機制,屬於面向切面編程(AOP)的一種運用,就是在service或者一個方法前,調用一個方法,或者在方法后,調用一個方法,比如動態代理就是攔截器的簡單實現,在調用方法前打印出字符串(或者做其它業務邏輯的操作),也可以在調用方法后打印出字符串,甚至在拋出異常的時候做業務邏輯的操作。
  由於攔截器是基於web框架的調用,因此可以使用Spring的依賴注入(DI)進行一些業務操作,同時一個攔截器實例在一個controller生命周期之內可以多次調用
  但是缺點是只能對controller請求進行攔截,對其他的一些比如直接訪問靜態資源的請求則沒辦法進行攔截處理
  登陸驗證
SpringMVC的配置文件
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.scorpios.atcrowdfunding.web.LoginInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.scorpios.atcrowdfunding.web.AuthInterceptor"></bean> </mvc:interceptor> </mvc:interceptors> 總結 (1)Filter需要在web.xml中配置,依賴於Servlet; (2)Interceptor需要在SpringMVC中配置,依賴於框架; (3)Filter的執行順序在Interceptor之前,具體的流程見下圖;

(4)區別:攔截器(Interceptor)是基於Java的反射機制,而過濾器(Filter)是基於函數回調
  從靈活性上說攔截器功能更強大些,Filter能做的事情,都能做,而且可以在請求前,請求后執行,比較靈活
  Filter主要是針對URL地址做一個編碼的事情、過濾掉沒用的參數、安全校驗(比較泛的,比如登錄不登錄之類),
  太細的話,還是建議用interceptor。不過還是根據不同情況選擇合適的。

 

3. Hibernate && Mybatis

1. Hibernate防止Sql注入

1.對參數名稱named parameter進行綁定:
    String hql = "from InventoryTask it where it.orgId=:orgId";
    Session session = getSession();
    Query query=session.createQuery(hql);
    query.setString("orgId",orgId);
    List list = query.list();
    if(list!=null&&list.size()!=0){
       return (InventoryTask)list.get(0);
    }else{
       return null;
    }
    
2.對參數位置positional parameter進行邦定:
      String hql = "from InventoryTask it where it.orgId=?,it.orgName";
      Session session = getSession();
      Query query=session.createQuery(hql);
      query.setString("0",orgId);
      query.setString(1,orgName)
      List list = query.list();
      if(list!=null&&list.size()!=0){
          return (InventoryTask)list.get(0);
      }else{
          return null;
      }
  
3.setParameter()方法:
    Query query=session.createQuery(hql); 
    query.setParameter(“name”,name,Hibernate.STRING);
4.setProperties()方法: Entity entity=new Entity(); entity.setXx(“xx”); entity.setYy(100); Query query=session.createQuery(“from Entity c where c.xx=:xx and c.yy=:yy ”);  query.setProperties(entity); 5.HQL拼接方法,這種方式是最常用,而且容易忽視且容易被注入的,通常做法就是對參數的特殊字符進行過濾,推薦使用 Spring工具包的StringEscapeUtils.escapeSql()方法對參數進行過濾:   import org.apache.commons.lang.StringEscapeUtils;   public static void main(String[] args) {     String str = StringEscapeUtils.escapeSql("'");     System.out.println(str);   }   StringEscapeUtils.escapeSql();    /**      * <p>Escapes the characters in a <code>String</code> to be suitable to pass to      * an SQL query.</p>      *      * <p>For example,      * <pre>statement.executeQuery("SELECT * FROM MOVIES WHERE TITLE='" +       *   StringEscapeUtils.escapeSql("McHale's Navy") +       *   "'");</pre>      * </p>      *      * <p>At present, this method only turns single-quotes into doubled single-quotes      * (<code>"McHale's Navy"</code> => <code>"McHale''s Navy"</code>). It does not      * handle the cases of percent (%) or underscore (_) for use in LIKE clauses.</p>      *      * see http://www.jguru.com/faq/view.jsp?EID=8881      * @param str  the string to escape, may be null      * @return a new String, escaped for SQL, <code>null</code> if null string input      */     public static String escapeSql(String str) {         if (str == null) {             return null;         }         return StringUtils.replace(str, "'", "''");     }   //輸出結果:''

2. Mybatis 防止Sql注入

 #{}:#{}是經過預編譯的,是安全的,相當於JDBC中的PreparedStatement。

    ${}:是未經過預編譯的,僅僅是取變量的值,是非安全的,存在SQL注入。

    在編寫MyBatis的映射語句時,盡量采用“#{xxx}”這樣的格式。若不得不使用“${xxx}”這樣的參數,要手工地做好過濾工作,來防止SQL注入攻擊。

3. Mybatis # $

#{} : 解析為一個 JDBC 預編譯語句(prepared statement)的參數標記符,一個 #{ } 被解析為一個參數占位符 。

${}: 僅僅為一個純碎的 string 替換,在動態 SQL 解析階段將會進行變量替換。

1、#將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。
  如:where username=#{username},如果傳入的值是111,那么解析成sql時的值為where username="111", 如果傳入的值是id,則解析成的sql為where username="id". 
2、$將傳入的數據直接顯示生成在sql中。
  如:where username=${username},如果傳入的值是111,那么解析成sql時的值為where username=111;
  如果傳入的值是;drop table user;,則解析成的sql為:select id, username, password, role from user where username=;drop table user;
3、#方式能夠很大程度防止sql注入,$方式無法防止Sql注入。
4、$方式一般用於傳入數據庫對象,例如傳入表名.
5、一般能用#的就別用$,若不得不使用“${xxx}”這樣的參數,要手工地做好過濾工作,來防止sql注入攻擊。
6、在MyBatis中,“${xxx}”這樣格式的參數會直接參與SQL編譯,從而不能避免注入攻擊。但涉及到動態表名和列名時,只能使用“${xxx}”這樣的參數格式。所以,這樣的參數需要我們在代碼中手工進行處理來防止注入。
【結論】在編寫MyBatis的映射語句時,盡量采用“#{xxx}”這樣的格式。若不得不使用“${xxx}”這樣的參數,要手工地做好過濾工作,來防止SQL注入攻擊。

4. Mybatis 實現 in

foreach語句實現IN查詢
/** foreach語句中, collection屬性的參數類型可以使:List、數組、map集合 ​ collection: 必須跟mapper.java中@Param標簽指定的元素名一樣 ​ item: 表示在迭代過程中每一個元素的別名,可以隨便起名,但是必須跟元素中的#{}里面的名稱一樣。    index:表示在迭代過程中每次迭代到的位置(下標)    open:前綴, sql語句中集合都必須用小括號()括起來 ​ close:后綴    separator:分隔符,表示迭代時每個元素之間以什么分隔 */
    1)如果是list,所以collention里就要寫list, 2)如果是array,則就要寫array。 3)當查詢的參數有多個時,有兩種方式可以實現,一種是使用@Param("xxx")進行參數綁定,另一種可以通過Map來傳參數。 4)如果是item表示的就是查詢的參數,因為是對象,所以就要獲取其屬性值,也就是object.userId。 
1. list
List<User> selectByIdList(List idList);
 
<select id="selectByIdList" resultMap="BaseResultMap">
    SELECT
    <include refid="Base_Column_List" />
    from t_user
    WHERE id IN
    <foreach collection="list" item="id" index="index" open="(" close=")" separator=",">
      #{id}
    </foreach>
</select>

2. array
List<User> selectByIdArray(String[] idList);
 
<select id="selectByIdArray" resultMap="BaseResultMap">
    SELECT
    <include refid="Base_Column_List" />
    from t_user
    WHERE id IN
    <foreach collection="array" item="id" index="index" open="(" close=")" separator=",">
      #{id}
    </foreach>
</select>

3. 多個參數:當查詢的參數有多個時,有兩種方式可以實現,一種是使用@Param("xxx")進行參數綁定,另一種可以通過Map來傳參數

1)@Param("xxx")方式 List
<User> selectByNameAndIdArray(@Param("name")String name, @Param("ids")String[] idList); <select id="selectByNameAndIdArray" resultMap="BaseResultMap"> SELECT <include refid="Base_Column_List" /> from t_user WHERE name=#{name,jdbcType=VARCHAR} and id IN <foreach collection="idList" item="id" index="index" open="(" close=")" separator=","> #{id} </foreach> </select> 2)Map方式 Map<String, Object> params = new HashMap<String, Object>(2); params.put("name", name); params.put("idList", ids); mapper.selectByIdMap(params); <select id="selectByIdMap" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from t_user where name = #{name} and ID in <foreach item="item" index="index" collection="idList" open="(" separator="," close=")"> #{item} </foreach> </select>

5. mybais count

resultType

mybatis返回count(*)的整數值

int selectNums();
<select id="selectNums" resultType="java.lang.Integer"> select count(*) from tableName </select>

6. mybatis中傳入String類型參數

1. 在接口參數里加上mybatis中的@param注解(優先)
List<User> findUserByName(@Param("name")String name);

<select id="findUserByName" parameterType="java.lang.String" resultType="com.entity.User">
    SELECT id,name FROM t_user where id = '1'
       <if test="name!= null and name!= ''">
           AND name LIKE concat('%',#{name},'%')
       </if>
</select> 2. 在xml的if里用"_parameter" 代表參數
List<User> findUserByName(String name);

<select id="findUserByName" parameterType="java.lang.String" resultType="com.entity.User">
    SELECT id,name FROM t_user  where id = '1'
       <if test="_parameter!= null and _parameter!= ''">
           AND name LIKE concat('%',#{name},'%')
       </if>
</select> _parameter不能區分多個參數,而@param能。所以@param能傳多個這樣的參數 
注意看,是在if test=驗證的時候發生的 “There is no getter for property named in ‘class java.lang.String’”,而並非是and username = #{username} 的時候發生的錯誤。

 

4. ThreadLocal

ThreadLocl 主要用於線程安全地共享某個變量

1. 底層

ThreadLocalMap
Entry(ThreadLocal<?>, Object)

什么是ThreadLocal?
/***
ThreadLocal類顧名思義可以理解為線程本地變量。
也就是說如果定義了一個ThreadLocal,每個線程往這個ThreadLocal中讀寫是線程隔離,互相之間不會影響的。
它提供了一種將可變數據通過每個線程有自己的獨立副本從而實現線程封閉的機制。 *
*/ 它大致的實現思路是怎樣的? /*** Thread類有一個類型為ThreadLocal.ThreadLocalMap的實例變量threadLocals,
也就是說每個線程有一個自己的ThreadLocalMap
ThreadLocalMap有自己的獨立實現,可以簡單地將它的key視作ThreadLocal,value為代碼中放入的值(實際上key並不是ThreadLocal本身,而是它的一個弱引用)。
每個線程在往某個ThreadLocal里塞值的時候,都會往自己的ThreadLocalMap里存,讀也是以某個ThreadLocal作為引用,在自己的map里找對應的key,從而實現了線程隔離。 *
*/

2. ThreadLocal和線程池

線程池(核心線程,銷毀線程)

副作用(臟數據,內存溢出)

ThreadLocl 主要用於線程安全地共享某個變量 ThreadLocl 主要會產生臟數據和內存泄露。這兩個問題通常是在線程池的線程中使用ThreadLocal 引發的,因為線程池有線程復用和內存常駐兩個特點。

1.臟數據 線程復用會產生臟數據。由於線程池會重用Thread對象,那么與Thread綁定的類靜態屬性也會被重用。
  如果在實現線程run() 方法中不顯示的調用remove() 清理與線程相關的ThreadLocal 信息。
  如果先一個線程不調用set() 設置初始值,那么就get() 到重用信息,包括ThreadLocl 所關聯線對象的值。 2.內存泄露 在源碼注釋中提示使用static 關鍵字來修改ThreadLocal。
  在此場景下,寄希望於ThreadLocal對象失去引用后,觸發弱引用機制來回收Entry 的Value 就不現實了。
  在上例中,如果不進行remove() 操作,那么這個線程執行完成后,通過ThreadLocal 對象持有的string對象是不會被釋放的。 以上兩個問題解決的辦法很簡單,就是每次用完ThreadLocal 時,必須調用remove() 方法清理

JAVA源碼系列之JAVA並發ThreadLocal

 

5. Volatile

 

 

 

6. synchronized

Lock與synchronized有以下區別:

  1. Lock是一個接口,而synchronized是關鍵字。
  2. synchronized會自動釋放鎖,而Lock必須手動釋放鎖。
  3. Lock可以讓等待鎖的線程響應中斷,而synchronized不會,線程會一直等待下去。
  4. 通過Lock可以知道線程有沒有拿到鎖,而synchronized不能。
  5. Lock能提高多個線程讀操作的效率。
  6. synchronized能鎖住類、方法和代碼塊,而Lock是塊范圍內的

 

7. CAS

8. CountDownLatch

 

https://www.cnblogs.com/haimishasha/p/11198482.html

9. 公平鎖 & 非公平鎖

公平鎖:FIFO 獲取不到鎖的時候,會自動加入隊列,等待線程釋放后,隊列的第一個線程獲取鎖

非公平鎖: 獲取不到鎖的時候,會自動加入隊列,等待線程釋放鎖后所有等待的線程同時去競爭

它們的差別在於非公平鎖會有更多的機會去搶占鎖。
可重入: 同一個線程可以反復獲取鎖多次,然后需要釋放多次
synchronized 是非公平鎖,可以重入。

ReentrantLock內部擁有一個Sync內部類,該內部類繼承自AQS,該內部類有兩個子類FairSync和NonfairSync,分別代表了公平鎖和非公平鎖,ReentrantLock默認使用非公平鎖
   /** * true 表示 ReentrantLock 的公平鎖 */ private ReentrantLock lock = new ReentrantLock(true);

 

10. HTTPS

應用層協議:HTTPS

 

 

 

 
        
一個HTTPS請求實際上包含了兩次HTTP傳輸,可以細分為8步。

1. 客戶端發起HTTPS請求:請求攜帶了瀏覽器支持的加密算法和哈希算法
  客戶端向服務器發起HTTPS請求,連接到服務器的443端口。 

2. 服務端的配置:服務器收到請求,選擇瀏覽器支持的加密算法和哈希算法。
  服務器端有一個密鑰對,即公鑰和私鑰,是用來進行非對稱加密使用的,服務器端保存着私鑰,不能將其泄露,公鑰可以發送給任何人。采用HTTPS協議的服務器必須要有一套數字證書。

3. 傳送證書:服務器將自己的CA 證書(公鑰)發送給客戶端。

  這個證書其實就是公鑰,只是包含了很多信息,如證書的頒發機構,過期時間等等。

4. 客戶端解析證書(TLS)

  客戶端收到服務器端的公鑰之后,會對公鑰進行檢查,驗證其合法性,
  如果發現發現公鑰有問題,那么HTTPS傳輸就無法繼續。
  如果公鑰合格,那么客戶端會生成一個隨機值,這個隨機值就是用於進行對稱加密的密鑰,我們將該密鑰稱之為client key,即客戶端密鑰,這樣在概念上和服務器端的密鑰容易進行區分。
  然后用服務器的公鑰對客戶端密鑰進行非對稱加密,這樣客戶端密鑰就變成密文了,
  至此,HTTPS中的第一次HTTP請求結束。
  這部分工作是有客戶端的TLS來完成的,首先會驗證公鑰是否有效,比如頒發機構,過期時間等等,如果發現異常,則會彈出一個警告框,提示證書存在問題。如果證書沒有問題,那么就生成一個隨即值。然后用證書對該隨機值進行加密。就好像上面說的,把隨機值用鎖頭鎖起來,這樣除非有鑰匙,不然看不到被鎖住的內容。
5. 傳送加密信息
  客戶端會發起HTTPS中的第二個HTTP請求,將加密之后的客戶端密鑰發送給服務器。   這部分傳送的是用證書加密后的隨機值R(私鑰),目的就是讓服務端得到這個隨機值R,以后客戶端和服務端的通信就可以通過這個隨機值R來進行加密解密了。
6. 服務端解密信息
  服務器接收到客戶端發來的密文之后,會用自己的私鑰對其進行非對稱解密,解密之后的明文就是客戶端密鑰(隨機數 R)
  然后把內容用客戶端密鑰隨機數 R進行對稱加密,這樣數據就變成了密文。   服務端用私鑰解密后,得到了客戶端傳過來的隨機值(私鑰),然后把內容通過該值進行對稱加密。
  所謂對稱加密就是,將信息和私鑰通過某種算法混合在一起,這樣除非知道私鑰,不然無法獲取內容,而正好客戶端和服務端都知道這個私鑰,所以只要加密算法夠彪悍,私鑰夠復雜,數據就夠安全。
7. 傳輸加密后的信息
  然后服務器將加密后的密文發送給客戶端。(服務器以隨機數 R 為密鑰把傳輸內容使用對稱加密算法加密並傳輸給瀏覽器)。   這部分信息是服務端用私鑰加密后的信息,可以在客戶端被還原
8. 客戶端解密信息
  客戶端收到服務器發送來的密文,用客戶端密鑰(隨機數 R)對其進行對稱解密,得到服務器發送的數據。這樣HTTPS中的第二個HTTP請求結束,整個HTTPS傳輸完成。   客戶端用之前生成的私鑰解密服務段傳過來的信息,於是獲取了解密后的內容。整個過程第三方即使監聽到了數據,也束手無策。

 

11. QPS過大怎么辦?

/**
盡量使用緩存,包括用戶緩存,信息緩存等,多花點內存來做緩存,可以大量減少與數據庫的交互,提高性能。

用jprofiler等工具找出性能瓶頸,減少額外的開銷。

優化數據庫查詢語句,減少直接使用hibernate等工具的直接生成語句(僅耗時較長的查詢做優化)。

優化數據庫結構,多做索引,提高查詢效率。

統計的功能盡量做緩存,或按每天一統計或定時統計相關報表,避免需要時進行統計的功能。

能使用靜態頁面的地方盡量使用,減少容器的解析(盡量將動態內容生成靜態html來顯示)。

解決以上問題后,使用服務器集群來解決單台的瓶頸問題。
*/

 

12. linux基本命令

1. Linux三劍客(grep、sed、awk)

Linux三劍客(grep、sed、awk)
grep:文本過濾(模式:pattern)工具,grep, egrep
sed:是一種流編輯器,它一次處理一行內容。處理時,把當前處理的行存儲在臨時緩沖區中
awk:報告生成器,格式化文本輸出,有多種版本:New awk(nawk),GNU awk( gawk)

-記住三個命令的運用形式
     grep    '字符'       文件
     sed     '命令'       文件
     awk    '條件{命令}'   文件
-單引號內就是正則表達式的用法


Linux下使用Shell截取文件一部分內容保存到新的文件中

1. 確定有自己業務有關的日志在文件中的行數

 grep -n "業務有關的關鍵字" filename

 根據關鍵字的搜索結果,綠色數字為關鍵字出現在文件中所在的行數,這樣就可以大概估算關鍵字出現的行的范圍了。

2. 截取指定行之間的日志到新的文件中 

 sed -n '開始行數,結束行數p'  待截取的文件  >> 保存的新文件 

 

2. 授權

chown:用來更改某個目錄或文件的用戶名和用戶組

chmod:用來更改某個目錄或文件的訪問權限

`ll`命令查看目錄下文件

"drwxr-xr-x" 可見一共有十位。-[rw-][r--][r--].其中第一個[-]代表的是類型,其中第一位為d代表目錄,每三位代表一個權限位

d:目錄
rwx: 可讀、可寫、可執行   2-4位代表所有者擁有的權限
r-x: 可讀、可執行        5-7位代表群組擁有的權限
r-x: 可讀、可執行        8-10位代表其他人擁有的權限

十進制表示
/**
r : 4 
w : 2
x : 1
- :  0 
/**

權限操作
/**
+ 表示添加權限
- 表示刪除權限
= 重置權限
修改文件權限
*/

位置
/**
u:代表文件所有者(user)
g: 代表所有者所在的群組(group)
o:代表其他人,但不是u和g(other)
a:a和一起指定ugo效果一樣
*/

/**
chmod o+w test.txt :表示給其他人授予寫test.txt這個文件的權限
chmod go-rw test.txt : 表示群組和其他人刪除對test.txt文件的讀寫權限
chmod ugo+r test.txt:所有人皆可讀取
chmod a+r text.txt:所有人皆可讀取
chmod ug+w,o-w text.txt:設為該檔案擁有者,與其所屬同一個群體者可寫入,但其他以外的人則不可寫入
chmod u+x test.txt: 創建者擁有執行權限 
chmod -R a+r ./www/ :將www下的所有檔案與子目錄皆設為任何人可讀取
chmod a-x test.txt :收回所有用戶的對test.txt的執行權限
chmod 777 test.txt: 所有人可讀,寫,執行
*/

修改目錄權限
/**
chmod 700   /opt/elasticsearch  #修改目錄權限
chmod -R 744 /opt/elasticsearch  #修改目目錄以下所有的權限   
-R             # 以遞歸方式更改所有的文件及子目錄
 chown 修改用戶組  修改 test.txt 目錄所屬用戶為 root,用戶組為 root
chown -R root:root test.txt   
-rw-r--r--. 1 root root   55 8月  24 17:25 test.txt
 */

常見權限
/**
-rw------- (600) 只有所有者才有讀和寫的權限。
-rw-r--r-- (644) 只有所有者才有讀和寫的權限,群組和其他人只有讀的權限。
-rw-rw-rw- (666) 每個人都有讀寫的權限
-rwx------ (700) 只有所有者才有讀,寫和執行的權限。
-rwx--x--x (711) 只有所有者才有讀,寫和執行的權限,群組和其他人只有執行的權限。
-rwxr-xr-x (755) 只有所有者才有讀,寫,執行的權限,群組和其他人只有讀和執行的權限。
-rwxrwxrwx (777) 每個人都有讀,寫和執行的權限
*/

實踐 在用Elasticsearch的時候是少不了給添加用戶授權
/**
chmod 400 test.txt        #修改text.txt為可讀文件
vi text.txt               #執行該命令后,該文件就無法進行寫入操作 提示下面信息
-- INSERT -- W10: Warning: Changing a readonly file
chmod 777 text.txt 
-rwxrwxrwx. 1 root root   55 8月  24 17:25 test.txt
chmod rwxr--r-- test.txt   #異常,不能使用該命令來修改權限
*/

 

3. root權限

方法一:修改 /etc/sudoers 文件,找到下面一行,把前面的注釋(#)去掉
## Allows people in group wheel to run all commands
%wheel    ALL=(ALL)    ALL
然后修改用戶,使其屬於root組(wheel),命令如下:
#usermod -g root tommy
修改完畢,現在可以用tommy帳號登錄,然后用命令 su – ,即可獲得root權限進行操作。

方法二:修改 /etc/sudoers 文件,找到下面一行,在root下面添加一行,如下所示:
## Allow root to run any commands anywhere
root    ALL=(ALL)     ALL
tommy ALL=(ALL) ALL
修改完畢,現在可以用tommy帳號登錄,然后用命令 sudo – ,即可獲得root權限進行操作。

方法三:修改 /etc/passwd 文件,找到如下行,把用戶ID修改為 0 ,如下所示:
tommy:x:0:33:tommy:/data/webroot:/bin/bash

 

4. 查看進程和端口

1、lsof -i:端口號
2、netstat -tunlp|grep 端口號
都可以查看指定端口被哪個進程占用的情況

lsof -i 用以顯示符合條件的進程情況,lsof(list open files)是一個列出當前系統打開文件的工具。
lsof -i:端口號,用於查看某一端口的占用情況,比如查看22號端口使用情況,lsof -i:22
netstat -tunlp用於顯示tcp,udp的端口和進程等相關情況
netstat -tunlp|grep 端口號,用於查看指定端口號的進程情況,如查看22端口的情況,netstat -tunlp|grep 22

 

5. 查看全局

1、查看CPU信息
# 總核數 = 物理CPU個數 X 每顆物理CPU的核數
# 總邏輯CPU數 = 物理CPU個數 X 每顆物理CPU的核數 X 超線程數

# 查看物理CPU個數
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

# 查看每個物理CPU中core的個數(即核數)
cat /proc/cpuinfo| grep "cpu cores"| uniq

# 查看邏輯CPU的個數
cat /proc/cpuinfo| grep "processor"| wc -l

# 查看CPU信息(型號)
cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c


#CPU負載信息,使用top 命令

2、查看內存信息
1)cat /proc/meminfo
2)free 命令

3、查看磁盤信息
1)fdisk -l
2)iostat -x 10    查看磁盤IO的性能

 

13. 數據庫索引最左原則

最左前綴原則
mysql建立多列索引(聯合索引)有最左前綴的原則,即最左優先,如:

如果有一個2列的索引(col1,col2),則已經對(col1)、(col1,col2)上建立了索引; 如果有一個3列索引(col1,col2,col3),則已經對(col1)、(col1,col2)、(col1,col2,col3)上建立了索引;

1、b+樹的數據項是復合的數據結構,比如(name,age,sex)的時候,b+樹是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最后得到檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道第一步該查哪個節點,因為建立搜索樹的時候name就是第一個比較因子,必須要先根據name來搜索才能知道下一步去哪里查詢。
2、比如當(張三,F)這樣的數據來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失,所以只能把名字等於張三的數據都找到,然后再匹配性別是F的數據了, 這個是非常重要的性質,即索引的最左匹配特性。(這種情況無法用到聯合索引)

關於最左前綴的使用,有下面兩條說明:
1. 最左前綴匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。
2. =和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式

 

14. B+樹

B+ 樹是一種樹數據結構,是一個n叉樹,每個節點通常有多個孩子,一顆B+樹包含根節點、內部節點和葉子節點。根節點可能是一個葉子節點,也可能是一個包含兩個或兩個以上孩子節點的節點。 

B+ 樹通常用於數據庫和操作系統的文件系統中。 NTFS, ReiserFS, NSS, XFS, JFS, ReFS 和BFS等文件系統都在使用B+樹作為元數據索引。 

B+ 樹的特點是能夠保持數據穩定有序,其插入與修改擁有較穩定的對數時間復雜度。 

B+ 樹元素自底向上插入。 

所有的葉子結點中包含了全部關鍵字的信息,及指向含有這些關鍵字記錄的指針,且葉子結點本身依關鍵字的大小自小而大的順序鏈接。(而B 樹的葉子節點並沒有包括全部需要查找的信息) 

所有的非終端結點可以看成是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。(而B 樹的非終節點也包含需要查找的有效信息)

/** B樹 
    每個節點都存儲key和data,所有節點組成這棵樹,並且葉子節點指針為null。
    B樹優點在於,由於B樹的每一個節點都包含key和value,因此經常訪問的元素可能離根節點更近,因此訪問也更迅速。

B+樹 
    只有葉子節點存儲data,葉子節點包含了這棵樹的所有鍵值,葉子節點不存儲指針。所有非終端節點看成是索引,節點中僅含有其子樹根節點最大(或最小)的關鍵字,不包含查找的有效信息。B+樹中所有葉子節點都是通過指針連接在一起。

   B+ 樹的優點在於:
    由於B+樹在內部節點上不包含數據信息,因此在內存頁中能夠存放更多的key。 數據存放的更加緊密,具有更好的空間局部性。因此訪問葉子節點上關聯的數據也具有更好的緩存命中率。
    B+樹的葉子結點都是相鏈的,因此對整棵樹的便利只需要一次線性遍歷葉子結點即可。而且由於數據順序排列並且相連,所以便於區間查找和搜索。而B樹則需要進行每一層的遞歸遍歷。相鄰的元素可能在內存中不相鄰,所以緩存命中性沒有B+樹好。

B和B+樹的區別在於,B+樹的非葉子結點只包含導航信息,不包含實際的值,所有的葉子結點和相連的節點使用鏈表相連,便於區間查找和遍歷。

總結:為什么使用B+樹?
    1.文件很大,不可能全部存儲在內存中,故要存儲到磁盤上 
    2.索引的結構組織要盡量減少查找過程中磁盤I/O的存取次數(為什么使用B-/+Tree,還跟磁盤存取原理有關,具體看下邊分析) 
    3.局部性原理與磁盤預讀,預讀的長度一般為頁(page)的整倍數,(在許多操作系統中,頁得大小通常為4k) 
    4.數據庫系統巧妙利用了磁盤預讀原理,將一個節點的大小設為等於一個頁,這樣 每個節點只需要一次I/O 就可以完全載入,(由於節點中有兩個數組,所以地址連續)。而紅黑樹這種結構, h 明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性。

為什么B+樹比B樹更適合做索引?
    1.B+樹磁盤讀寫代價更低: B+的內部結點並沒有指向關鍵字具體信息的指針,即內部節點不存儲數據。因此其內部結點相對B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那么盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的需要查找的關鍵字也就越多。相對來說IO讀寫次數也就降低了。
    2.B+tree的查詢效率更加穩定: 由於非終結點並不是最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查找必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。

在MySQL中,最常用的兩個存儲引擎是MyISAM和InnoDB,它們對索引的實現方式是不同的。
    MyISAM data存的是數據地址。索引是索引,數據是數據。
    InnoDB data存的是數據本身。索引也是數據。
*/

 

15. GC

JVM系列之五:垃圾回收

1. 互相引用會不會清除?

循環引用,就看這個循環引用是否掛在根上,
假設掛在根上且這個根還被JVM的Java代碼所運行的話,就不會GC掉,假設說這個根已經被釋放掉了。這個對象不掛在跟上了。那個這個對象就會被GC掉。

 

2. java dump文件怎么生成和分析-JVM調優

1. 查看整個JVM內存狀態 

jmap -heap [pid]

2. 查看JVM堆中對象詳細占用情況
jmap
-histo [pid] 3. 導出整個JVM 中內存信息,可以利用其它工具打開dump文件分析,例如jdk自帶的visualvm工具 jmap -dump:file=文件名.dump [pid] jmap -dump:format=b,file=文件名 [pid] format=b指定為二進制格式文件

3. 可視化工具有哪些

1.JConsole
  JConsole工具在JDK/bin目錄下,啟動JConsole后,將自動搜索本機運行的jvm進程,不需要jps命令來查詢指定。雙擊其中一個jvm進程即可開始監控,也可使用“遠程進程”來連接遠程服務器。
  進入JConsole主界面,有“概述”、“內存”、“線程”、“類”、“VM摘要”和"Mbean"六個頁簽:
2.VisualVM
  VisualVM是一個集成多個JDK命令行工具的可視化工具。VisualVM基於NetBeans平台開發,它具備了插件擴展功能的特性,通過插件的擴展,可用於顯示虛擬機進程及進程的配置和環境信息(jps,jinfo),監視應用程序的CPU、GC、堆、方法區及線程的信息(jstat、jstack)等。VisualVM在JDK/bin目錄下。
3.jprofiler

 

16. 去除ArrayList里面的偶數

迭代器

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class A1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(6);
        System.out.println("刪除前:" + list);

        int size = list.size();
        Iterator<Integer> it = list.iterator();

        for (int i = 0; i < size; i++) {
            if (it.next() % 2 == 0) {
                it.remove();
            }
        }

        System.out.println("刪除后:" + list);
    }
}

 

二面

1. GC

JVM系列之五:垃圾回收

 CMS: 標記清除

2. 數據庫隔離級別以及實現原理

美團點評面試20190515

對應隔離級別
    1.READ UNCOMMITTED:讀未提交,不處理,會出現臟讀,不可重復讀,幻讀。
    2.READ COMMITTED:讀已提交,只讀提交的數據,無臟讀,但這種級別會出現讀取舊數據的現象,不可重復讀,大多數數據庫系統的默認隔離級別。
    3.REPEATABLE READ:可重復讀,加行鎖,兩次讀之間不會有修改,無臟讀無重復讀;保證了每行的記錄的結果是一致的。但是無法解決幻讀
    4.SERIALIZABLE: 串行化,加表鎖,強制事務串行執行,無所有問題,不會出現臟讀,不可重復讀,幻讀。由於他大量加上鎖,導致大量的請求超時,因此性能會比較低下,需要數據一致性且並發量不需要那么大的時候才可能考慮這個隔離級別。

隔離級別原理
READ_UNCOMMITED 的原理:
    1,事務對當前被讀取的數據不加鎖;
    2,事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級共享鎖,直到事務結束才釋放。  
   // 1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,能讀到事務2對該記錄的修改版本,即使該修改尚未被提交。
   // 2,事務1更新某行記錄時,事務2不能對這行記錄做更新,直到事務1結束。

READ_COMMITED 的原理:
    1,事務對當前被讀取的數據加 行級共享鎖(當讀到時才加鎖),一旦讀完該行,立即釋放該行級共享鎖;
    2,事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。
   // 1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,讀到的只能是事務2對其更新前的版本,要不就是事務2提交后的版本。
   // 2,事務1更新某行記錄時,事務2不能對這行記錄做更新,直到事務1結束。

REPEATABLE READ 的原理:
    1,事務在讀取某數據的瞬間(就是開始讀取的瞬間),必須先對其加 行級共享鎖,直到事務結束才釋放;
    2,事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。 
    // 1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,讀到的仍然是第一次讀取的那個版本。
    // 2,事務1更新某行記錄時,事務2不能對這行記錄做更新,直到事務1結束。

SERIALIZABLE 的原理:
    1,事務在讀取數據時,必須先對其加 表級共享鎖 ,直到事務結束才釋放;
    2,事務在更新數據時,必須先對其加 表級排他鎖 ,直到事務結束才釋放。
    // 1,事務1正在讀取A表中的記錄時,則事務2也能讀取A表,但不能對A表做更新、新增、刪除,直到事務1結束。
    // 2,事務1正在更新A表中的記錄時,則事務2不能讀取A表的任意記錄,更不可能對A表做更新、新增、刪除,直到事務1結束。

數據庫鎖// 數據庫鎖出現的目的:處理並發問題 鎖分類 從數據庫系統角度分為三種:排他鎖、共享鎖、更新鎖。 從程序員角度分為兩種:一種是悲觀鎖,一種樂觀鎖。 悲觀鎖按使用性質划分:排他鎖、共享鎖、更新鎖。 悲觀鎖按作用范圍划分:行鎖、表鎖。 樂觀鎖實現方式:版本號,時間戳。 數據庫規定同一資源上不能同時共存共享鎖和排他鎖。 一、悲觀鎖(Pessimistic Lock) 顧名思義,很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人拿這個數據就會block(阻塞),直到它拿鎖。傳統的關系數據庫里用到了很多這種鎖機制,比如行鎖、表鎖、讀鎖、寫鎖等,都是在操作之前先上鎖。 1. 共享鎖(Share Lock) S鎖,也叫讀鎖,用於所有的只讀數據操作。共享鎖是非獨占的,允許多個並發事務讀取其鎖定的資源。 性質 1. 多個事務可封鎖同一個共享頁; 2. 任何事務都不能修改該頁; 3. 通常是該頁被讀取完畢,S鎖立即被釋放。 // 在SQL Server中,默認情況下,數據被讀取后,立即釋放共享鎖。 // 例如,執行查詢語句“SELECT * FROM my_table”時,首先鎖定第一頁,讀取之后,釋放對第一頁的鎖定,然后鎖定第二頁。這樣,就允許在讀操作過程中,修改未被鎖定的第一頁。 // 例如,語句“SELECT * FROM my_table HOLDLOCK”就要求在整個查詢過程中,保持對表的鎖定,直到查詢完成才釋放鎖定。 2. 排他鎖(Exclusive Lock) X鎖,也叫寫鎖,表示對數據進行寫操作。如果一個事務對對象加了排他鎖,其他事務就不能再給它加任何鎖了。 性質 1. 僅允許一個事務封鎖此頁; 2. 其他任何事務必須等到X鎖被釋放才能對該頁進行訪問; 3. X鎖一直到事務結束才能被釋放。 // 產生排他鎖的SQL語句如下:select * from ad_plan for update; 3. 更新鎖 U鎖,在修改操作的初始化階段用來鎖定可能要被修改的資源,這樣可以避免使用共享鎖造成的死鎖現象。 // 因為當使用共享鎖時,修改數據的操作分為兩步: 1. 首先獲得一個共享鎖,讀取數據, 2. 然后將共享鎖升級為排他鎖,再執行修改操作。 這樣如果有兩個或多個事務同時對一個事務申請了共享鎖,在修改數據時,這些事務都要將共享鎖升級為排他鎖。這時,這些事務都不會釋放共享鎖,而是一直等待對方釋放,這樣就造成了死鎖。 // 如果一個數據在修改前直接申請更新鎖,在數據修改時再升級為排他鎖,就可以避免死鎖。 性質 1. 用來預定要對此頁施加X鎖,它允許其他事務讀,但不允許再施加U鎖或X鎖; 2. 當被讀取的頁要被更新時,則升級為X鎖; 3. U鎖一直到事務結束時才能被釋放。 4. 行鎖 鎖的作用范圍是行級別。 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,並發度也最高。 5. 表鎖 鎖的作用范圍是整張表。 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,並發度最低。 6. 頁面鎖 頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,並發度一般。 // 數據庫能夠確定那些行需要鎖的情況下使用行鎖,如果不知道會影響哪些行的時候就會使用表鎖。 // 舉個例子,一個用戶表user,有主鍵id和用戶生日birthday。 // 當你使用update … where id=?這樣的語句時,數據庫明確知道會影響哪一行,它就會使用行鎖; // 當你使用update … where birthday=?這樣的的語句時,因為事先不知道會影響哪些行就可能會使用表鎖。 二、樂觀鎖(Optimistic Lock) 顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以,不會上鎖。但是在更新的時候會判斷一下在此期間別人有沒有更新這個數據,可以使用版本號等機制。 1. 版本號(version) 版本號(記為version):就是給數據增加一個版本標識,在數據庫上就是表中增加一個version字段,每次更新把這個字段加1,讀取數據的時候把version讀出來,更新的時候比較version,如果還是開始讀取的version就可以更新了,如果現在的version比老的version大,說明有其他事務更新了該數據,並增加了版本號,這時候得到一個無法更新的通知,用戶自行根據這個通知來決定怎么處理,比如重新開始一遍。這里的關鍵是判斷version和更新兩個動作需要作為一個原子單元執行,否則在你判斷可以更新以后正式更新之前有別的事務修改了version,這個時候你再去更新就可能會覆蓋前一個事務做的更新,造成第二類丟失更新,所以你可以使用update … where … and version=”old version”這樣的語句,根據返回結果是0還是非0來得到通知,如果是0說明更新沒有成功,因為version被改了,如果返回非0說明更新成功。 2. 時間戳(使用數據庫服務器的時間戳) 時間戳(timestamp):和版本號基本一樣,只是通過時間戳來判斷而已,注意時間戳要使用數據庫服務器的時間戳不能是業務系統的時間。 3. 待更新字段 待更新字段:和版本號方式相似,只是不增加額外字段,直接使用有效數據字段做版本控制信息,因為有時候我們可能無法改變舊系統的數據庫表結構。假設有個待更新字段叫count,先去讀取這個count,更新的時候去比較數據庫中count的值是不是我期望的值(即開始讀的值),如果是就把我修改的count的值更新到該字段,否則更新失敗。java的基本類型的原子類型對象如AtomicInteger就是這種思想。 4. 所有字段 所有字段:和待更新字段類似,只是使用所有字段做版本控制信息,只有所有字段都沒變化才會執行更新。

 

3. 樂觀鎖 & 悲觀鎖

悲觀鎖
總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉讓給其它線程)。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現。

樂觀鎖
總是假設最好的情況,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫提供的類似於write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。

兩種鎖的使用場景
從上面對兩種鎖的介紹,我們知道兩種鎖各有優缺點,不可認為一種好於另一種,像樂觀鎖適用於寫比較少的情況下(多讀場景),即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果是多寫的情況,一般會經常產生沖突,這就會導致上層應用會不斷的進行retry,這樣反倒是降低了性能,所以一般多寫的場景下用悲觀鎖就比較合適。

 

4. 計算機網絡

OSI七層

5. 逆時針輸出二維矩陣

可以參考 leetcode54螺旋矩陣:https://leetcode-cn.com/problems/spiral-matrix/

class Solution {
    public List < Integer > spiralOrder(int[][] matrix) {
        List ans = new ArrayList();
        if (matrix.length == 0)
            return ans;
        int r1 = 0, r2 = matrix.length - 1;
        int c1 = 0, c2 = matrix[0].length - 1;
        while (r1 <= r2 && c1 <= c2) {
            for (int c = c1; c <= c2; c++) ans.add(matrix[r1][c]);
            for (int r = r1 + 1; r <= r2; r++) ans.add(matrix[r][c2]);
            if (r1 < r2 && c1 < c2) {
                for (int c = c2 - 1; c > c1; c--) ans.add(matrix[r2][c]);
                for (int r = r2; r > r1; r--) ans.add(matrix[r][c1]);
            }
            r1++;
            r2--;
            c1++;
            c2--;
        }
        return ans;
    }
}

 

三面:項目難點

1. 智能體現在哪里?

2. 項目難點是什么?

3. 敏捷開發和傳統開發區別是什么?

4. 項目管理的角度提高效率?

5. 印象最深的一次面試?

6. 建議:復盤,及時反省,及時總結,主動分享,揚長避短。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM