細節決定成敗(一)

2020-08-12 14:19:47

細節決定成敗(一)

ScheduledThreadPool 執行週期性任務,假設某次出現了異常,週期任務還會繼續嗎?

檢視ScheduledExecutorService介面裏的scheduleWithFixedDelay方法的jdk文件,有描述如下: If any execution of the task encounters an exception, subsequent executions are suppressed. 如果在任務的執行中遇到異常,後續執行被取消。 ScheduledThreadPoolExecutor的最優實踐:將所有執行程式碼用try-catch包裹。


實現runnable是不是可以說明使用了組合的方式要比繼承的方式要好?

是的。


synchronized同步程式碼塊爲什麼會有兩個monitorexit?

public class SynTest {
    public void synBlock() {
        synchronized (this) {
            System.out.println("lagou");
        }
    }
}
//javap -verbose SynTest.class,就可以看到對應的反彙編內容。
  public void synBlock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #3                      // String lagou
         9: invokevirtual #4               // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: aload_1
        13: monitorexit
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit
        20: aload_2
        21: athrow
        22: return

synchronized 程式碼塊實際上多了 monitorenter 和 monitorexit 指令,的第3、13、19行指令分別對應的是 monitorenter 和 monitorexit。這裏有一個 monitorenter,卻有兩個 monitorexit 指令的原因是:JVM要保證每個monitorenter必須有與之對應的monitorexit,monitorenter指令被插入到同步程式碼塊的開始位置,而monitorexit需要插入到方法正常結束處和異常處兩個地方,這樣就可以保證拋異常的情況下也能釋放鎖。


synchronized同步方法是什麼原理?

public synchronized void synMethod() {
 
}
//對應的反彙編指令如下所示。
  public synchronized void synMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 16: 0

可以看出,被synchronized修飾的方法會flags裏面有一個ACC_SYNCHRONIZED標誌。當某個執行緒要存取方法的時候,會首先檢查方法是否有SCC_SYNCHRONIZED標誌,如果有則需要先獲得monitor鎖(如果不是static修飾,就獲取物件鎖,如果是static修飾就獲取類鎖),然後才能 纔能開始執行方法體,方法執行之後再釋放monitor鎖。


ReentrantLock的tryLock()是否會有公平行爲?

否,當有執行緒執行 tryLock() 方法的時候,一旦有執行緒釋放了鎖,那麼這個正在 tryLock 的執行緒就能獲取到鎖,即使設定的是公平鎖模式,即使在它之前已經有其他正在等待佇列中等待的執行緒,簡單地說就是 tryLock 可以插隊。

//看原始碼可知
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

這裏呼叫的就是 nonfairTryAcquire(),表明瞭是不公平的,和鎖本身是否是公平鎖無關。


ReentrantLock設定爲公平鎖有什麼優缺點?

優點:各個執行緒公平平等,每個執行緒在等待一段時間後,總能有執行的機會。

缺點:在於整體執行速度更慢,吞吐量更小。


ReentrantLock設定爲非公平鎖有什麼優缺點?

優點:在於整體執行速度更快,吞吐量更大。

缺點:可能產生執行緒飢餓問題,也就是說如果一直有執行緒插隊,那麼在等待佇列中的執行緒可能長時間得不到執行。


Kafka如何設計達到高吞吐量、低延遲?

Kafka訊息發送時,

  • Kafka每次寫入操作都只是把數據寫入到操作系統的頁快取中,由操作系統自己把頁快取中的數據寫回磁碟,操作系統頁快取是在記憶體中分配,訊息寫入速度非常快。
  • Kafka寫入操作採用追加方式,只能在日誌檔案末尾追加寫入新的訊息,且不允許修改已寫入的訊息,而磁碟順序存取操作速度可與記憶體的隨機存取相媲美。

Kafka消費端消費時,Kafka在讀取訊息時會首先嚐試從OS的頁快取中讀取,如果命中便把訊息經頁快取直接發送到網路的socket說。
概括的講:

  • 大量使用操作系統頁快取,記憶體操作速度快且命中率搞。
  • Kafka不直接參與物理I/O操作,而是交給最擅長此事的操作系統來完成。
  • 採用追加寫入方式,摒棄了緩慢地磁碟隨機讀/寫操作。
  • 使用以sendfile爲代表的零拷貝技術加強網路間的數據傳輸效率。

------摘自《Apache kafka實戰》


Spring框架中IOC容器有哪幾種?

BeanFactory - BeanFactory 就像一個包含 bean 集合的工廠類。它會在用戶端
要求時範例化 bean。

ApplicationContext - ApplicationContext 介面擴充套件了 BeanFactory 介面。它
在 BeanFactory 基礎上提供了一些額外的功能。


Spring啓動IOC容器的三種方式

https://blog.csdn.net/cuipp0509/article/details/78544497


關於Java8中提供的四個核心函數式介面的描述?

第一種:Consumer:消費型介面

void accept(T t);

第二種:Supplier:供給型介面

T get();

第三種:Function<T,R>:函數型介面

R apply(T t);

第四種:Predicate:斷言型介面

boolean test(T t)

//Consumer<T>:消費型介面
@Test
public void test1(){
    happy(10000,(m)->System.out.println("你們哥喜歡大寶劍,每次消費:"+m+"元"));
}
public void happy(double money, Consumer<Double> consumer){
    consumer.accept(money);
}

//Supplier<T>:供給型介面
@Test
public void test2(){
    List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
    for (Integer num:numList) {
        System.out.println(num);
    }
}
//需求:產生指定個數整數,並放入集閤中
public List<Integer> getNumList(int num, Supplier<Integer> supplier){
    List<Integer> list=new ArrayList<>();
    for (int i=0;i<num;i++){
        Integer n = supplier.get();
        list.add(n);
    }
    return list;
}

//Function<T,R>:函數型介面
@Test
public void test3(){
    String newStr = strHandler("\t\t\t 我大北大荒威武  ", (str) -> str.trim());
    System.out.println(newStr);

    String subStr = strHandler("我大北大荒威武", (str) -> str.substring(2, 5));
    System.out.println(subStr);
}
//需求:用於處理字串
public String strHandler(String str, Function<String,String> function){
    return function.apply(str);
}

//Predicate<T>:斷言型介面
@Test
public void test4() {
    List<String> list = Arrays.asList("hello", "atguigu", "Lambda", "www", "ok");
    List<String> strList = filterStr(list, (s) -> s.length() > 3);
    for (String str :strList) {
        System.out.println(str);
    }
}

//需求:將滿足條件的字串,放入集閤中
public List<String> filterStr(List<String> list, Predicate<String> predicate) {
    List<String> strlist = new ArrayList<>();
    for (String str : list) {
        if (predicate.test(str)) {
            strlist.add(str);
        }
    }
    return strlist;
}

Java Enum類中爲什麼建構函式必須是私有的?

列舉被設計成是單例模式,即列舉型別會由JVM在載入的時候,範例化列舉物件,你在列舉類中定義了多少個就會範例化多少個,JVM爲了保證每一個列舉類元素的唯一範例,是不會允許外部進行new的,所以會把建構函式設計成private,防止使用者生成範例,破壞唯一性。

列舉型別是單例模式的。你需要範例化一次,然後再整個程式之中就可以呼叫他的方法和成員變數了。列舉型別使用單例模式是因爲他的值是固定的,不需要發生改變。

https://blog.csdn.net/songxinfeng1989/article/details/104021930


下面 下麪哪種許可權是同一包可以存取,不同包的子類可以存取,不同包的非子類不可以存取?

  • A. private

  • B. default

  • C. protected

  • D. public

image


Maven的倉庫分爲哪兩大類

Maven倉庫分爲:本地倉庫+遠端倉庫兩大類

遠端倉庫又分爲:中央倉庫+私服+其它公共遠端倉庫


Spring框架中有哪些不同類型的事件?

Spring 提供了以下5種標準的事件:

(1)上下文更新事件(ContextRefreshedEvent):在呼叫ConfigurableApplicationContext 介面中的refresh()方法時被觸發。

(2)上下文開始事件(ContextStartedEvent):當容器呼叫ConfigurableApplicationContext的Start()方法開始/重新開始容器時觸發該事件。

(3)上下文停止事件(ContextStoppedEvent):當容器呼叫ConfigurableApplicationContext的Stop()方法停止容器時觸發該事件。

(4)上下文關閉事件(ContextClosedEvent):當ApplicationContext被關閉時觸發該事件。容器被關閉時,其管理的所有單例Bean都被銷燬。

(5)請求處理事件(RequestHandledEvent):在Web應用中,當一個http請求(request)結束觸發該事件。

如果一個bean實現了ApplicationListener介面,當一個ApplicationEvent 被髮布以後,bean會自動被通知。


JDBC連線池實現方式?

1.自定義

2.DBCP開源連線池

3.C3P0開源連線池


SVN

Diff和Diff with previous version可以檢視某個檔案的修改資訊。

Repo-browser爲程式碼庫瀏覽器,

Show log 爲檢視日誌。


儲存過程跟動態sql相比有如下優點:

1、 儲存過程允許標準組件式程式設計

儲存過程在被建立以後可以在程式中被多次呼叫而不必重新編寫該儲存過程的SQL
語句而且數據庫專業人員可隨時對儲存過程進行修改但對應用程式原始碼毫無影響因
爲應用程式原始碼只包含儲存過程的呼叫語句從而極大地提高了程式的可移植性

2 、儲存過程能夠實現較快的執行速度

如果某一操作包含大量的Transaction-SQL 程式碼或分別被多次執行那麼儲存過程要
比批次處理的執行速度快很多因爲儲存過程是預編譯的在首次執行一個儲存過程時查
詢優化器對其進行分析優化並給出最終被存在系統表中的執行計劃而批次處理的Transaction-
SQL 語句在每次執行時都要進行編譯和優化因此速度相對要慢一些

3 、儲存過程能夠減少網路流量

對於同一個針對數據數據庫物件的操作如查詢修改如果這一操作所涉及到的
Transaction-SQL 語句被組織成一儲存過程那麼當在客戶計算機上呼叫該儲存過程時
網路中傳送的只是該呼叫語句否則將是多條SQL 語句從而大大增加了網路流量降
低網路負載

4、 儲存過程可被作爲一種安全機制 機製來充分利用

系統管理員通過對執行某一儲存過程的許可權進行限制從而能夠實現對相應的數據訪
問許可權的限制避免非授權使用者對數據的存取保證數據的安全


MyBatis什麼情況用註解,什麼情況用xml系結?

註解使用情況:Sql語句簡單時

xml系結使用情況:xml系結 (@RequestMap用來系結xml檔案)


在使用Git進行開發時,發現圖片檔案image.png發生衝突,那麼如何檢視別人的版本?()

當目錄merge出現confilct時,可以使用git ls-files -s檢視當前staged的object的狀態。可以看到test檔案出現了3個stage的狀態,分別爲1,2,3。而沒有出現衝突的檔案的stage狀態爲0. 針對存在confilict的檔案可以: git show :1:test # 檢視衝突檔案的common ancester版本 git show :2:test # 檢視本地branch中的版本 git show :3:test # 檢視遠端branch中的版本


maven中,對於一個多模組專案,管理專案依賴的版本是:

通過在父模組中宣告dependencyManagement和pluginManagement, 然後讓子模組通過元素指定父模組,這樣子模組在定義依賴是就可以只定義groupId和artifactId,自動使用父模組的version,這樣統一整個專案的依賴的版本


使用Git時,如果已經將改變的檔案加入暫存區,但是突然又不想提交其中的service.java檔案,那麼該如何操作?

git reset – filename 用於使用HEAD中的filename覆蓋index中的版本。


對於使用 upstart 的系統而言(Ubuntu 14.04、Debian 7 Wheezy),編輯哪個檔案,在其中的 DOCKER_OPTS 中設定加速器地址?

對於使用 upstart 的系統而言,編輯 /etc/default/docker 檔案,在其中的 DOCKER_OPTS 中設定加速器地址:

DOCKER_OPTS="–registry-mirror=https://registry.docker-cn.com"
重新啓動服務。

對於使用 systemd 的系統(Ubuntu 16.04+、Debian 8+、CentOS 7),編輯哪個檔案,在其中的 DOCKER_OPTS 中設定加速器地址?

請在/etc/docker/daemon.json 中寫入如下內容(如果檔案不存在請新建該檔案)

{
  "registry-mirrors": [
    "https://registry.docker-cn.com"
  ]
}

注意,一定要保證該檔案符合 json 規範,否則 Docker 將不能啓動。

之後重新啓動服務。

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

要把映象goldberg重新命名爲mysqlberg

docker rename goldberg mysqlberg

swarm的排程模組的第一階段,過濾器有幾種?

5種

Constraints,約束過濾器
Affnity,親和性過濾器
Dependency,依賴過濾器
Health filter,會根據節點狀態進行過濾
Ports filter,會根據埠的使用情況過濾

Docker用ADD指令複製檔案時,下載後的檔案許可權自動設爲?

設定爲:600

在映象的構建過程中 Docker 會遍歷 Dockerfile 檔案中的所有指令,非同步執行。

錯誤,同步執行

阻塞與非阻塞佇列

image

阻塞佇列 非阻塞佇列
ArrayBlockingQueue ConcurrentLinkedQueue
LinkedBlockingQueue
SynchronousQueue
DelayQueue
PriorityQueue
LinkedTransferQueue

阻塞佇列包含哪些常用的方法?add、offer、put 等方法的區別?

\ 返回特殊值 拋異常 阻塞
新增元素 offer (true/false) add (IllegalStateException) put
刪除首部元素 poll (null) remove (NoSuchElementException) take
返回首部元素 peek (null) element (NoSuchElementException)

offer與poll還有對應的帶超時時間的過載方法

offer(E e, long tiomeout, TimeUnit unit)
poll(long tiomeout, TimeUnit unit)

SynchronousQueue的容量是多少?

++容量爲 0++ ,因爲SynchronousQueue不需要去持有元素,它所做的就是直接傳遞(direct handoff)。由於每次需要傳遞的時候,SynchronousQueue會把元素直接從生產者傳給消費者,在此期間並不需要做儲存。

所以它的peek()方法永遠返回 null

它的size()方法永遠返回 0

它的isEmpty()方法永遠返回 true


PriorityBlockingQueue 優先順序佇列

它成員變數裡只有一個Condition:

private final Condition notEmpty;

因爲它是無界佇列,所以只需要標識是否爲空的,不需要notFull,所以,它的put()方法永遠不會阻塞。


DelayQueue 延遲佇列

它是無界佇列,放入的元素必須實現Delayed介面,而Delayed介面又繼承了Comparable介面,所以自然就擁有了比較和排序的能力

public interface Delayed entendx Comparable<Delayed>{
    // 返回的是「還剩下多久的延遲時間纔會被執行」
    long getDelay(TimeUnit unit);
}

DelayQueue內部使用了PriorityQueue的能力來進行排序,減少了開發量,避免「重複造輪子」。


StringBuilder原理

  • 1.StringBuffer()的初始容量可以容納16個字元,當該物件的實體存放的字元的長度大於16時,實體容量就自動增加。StringBuffer物件可以通過length()方法獲取實體中存放的字元序列長度,通過capacity()方法來獲取當前實體的實際容量。
  • 2.StringBuffer(int size)可以指定分配給該物件的實體的初始容量參數爲參數size指定的字元個數。當該物件的實體存放的字元序列的長度大於size個字元時,實體的容量就自動的增加。以便存放所增加的字元。
  • 3.StringBuffer(String s)可以指定給物件的實體的初始容量爲參數字串s的長度額外再加16個字元。當該物件的實體存放的字元序列長度大於size個字元時,實體的容量自動的增加,以便存放所增加的字元。

StringBuilder是採用字元陣列來存放實際數據,當呼叫append(**)方法時,會先判斷追加的字串長度是否大於當前StringBuilder內的字元陣列長度,如果沒有超過,則直接新增,如果超過了就需要擴容。

擴容是建立新的陣列,先嚐試擴容爲舊陣列的兩倍+2,在判斷一下,容量是否足夠,不夠就直接擴容到需要的容量大小。

imageimage


JVM內部採用了StringBuffer來連線字串了,我們爲什麼不直接用「+」來連線字串?

因爲每次執行「+」操作時jvm都要new一個StringBuffer物件來處理字串的連線,這在涉及很多的字串連線操作時開銷會很大


日期時間

  • 1、日期格式化時。傳入pattern中表示年份統一使用小寫y,

日期格式化時,yyyy表示當天所在的年,而大寫的YYYY代表是week in which year(JDK7中引入的概念),意思是當天所在的周屬於的年份,一週從週日開始,週六結束,只要本週跨年,返回的就是下一年。

new SimpleDateFormat(「yyyy-MM-dd HH-mm-ss」)

  • 2、在日期格式中分清楚大寫的M和小寫的m,大寫的H和小寫的h分別指代的意義
  1. 表示月份是大寫的M;
  2. 表示分鐘則是小寫的m;
  3. 24小時制的是大寫的H;
  4. 12小時制的是小寫的h.

*使用集合轉陣列的方法,必須使用集合的toArray(T[] array),傳入是是型別完全一致、長度爲0的空陣列。

因爲直接使用toArray()無參方法的返回值是一個Object[]類,若強轉其它型別陣列將出現ClassCastException異常。

最優的方式就是這樣:

List<String> list = new Array<>();
list.add("liu");
list.add("yan");
list.add("bin");
String[] array = list.toArray(new String[0]);

說明:使用toArray(T[] array)帶參方法,陣列空間大小的length:

  • 1、等於0,動態建立與size相同的陣列,效能最好。
  • 2、大於0但小於size,重新建立大小等於size的陣列,增加GC負擔。
  • 3、等於size,在高併發情況下,陣列建立完成之後,size正在變大的情況下,負面影響與2相同。或者size正在變小,負面影響與4相同。
  • 4、大於size,空間浪費,且在size處插入null指,存在NPE隱患。

在使用Collection介面如何實現類的addAll()方法時,都要對輸入的集合參數進行NPE判斷。

說明:在Array#addAll()方法的第一行程式碼即Object[] a = c.toArray(); 其中c爲輸入集合參數,如果爲null,則直接拋出異常。


使用工具類Arrays.asList()把陣列轉換爲集合時,不能使用其修改集合相關的方法,它的add/remove/clear方法會拋出UnsupportedOperationException異常。

asList的返回物件是Arrays內部類,並沒有實現集合的修改方法。Arrays.asList體現的是適配器模式,只是轉換介面,後臺的數據仍是陣列。

String[] str = new String[]{"ni","hao","ya"};
List list = Arrays.asList(str);
  • 第一種情況:list.add(「sss」);執行時異常
  • 第二種情況:str[0] = 「lalala」;也會隨之修改,反之亦然。

*ThreadLocal 是用來解決共用資源的多執行緒存取的問題嗎?

答案很明確——不是,ThreadLocal 並不是用來解決共用資源問題的。雖然 ThreadLocal 確實可以用於解決多執行緒情況下的執行緒安全問題,但其資源並不是共用的,而是每個執行緒獨享的。所以這道題其實是有一定陷阱成分在內的。

ThreadLocal 解決執行緒安全問題的時候,相比於使用「鎖」而言,換了一個思路,把資源變成了各執行緒獨享的資源,非常巧妙地避免了同步操作。具體而言,它可以在 initialValue 中 new 出自己執行緒獨享的資源,而多個執行緒之間,它們所存取的物件本身是不共用的,自然就不存在任何併發問題。這是 ThreadLocal 解決併發問題的最主要思路。

如果我們把放到 ThreadLocal 中的資源用 static 修飾,讓它變成一個共用資源的話,那麼即便使用了 ThreadLocal,同樣也會有執行緒安全問題。