1. 程式人生 > 實用技巧 >記錄一次電話面試

記錄一次電話面試

執行緒安全問題

1、問題描述
為什麼java有多執行緒安全問題?JavaScriptRedis卻沒有執行緒安全問題?

2、我的回答
因為java中允許多個執行緒可以共同操作某個物件。
線上程模型中,多個執行緒可以操作共享物件,假設一個執行緒A將共享物件從1變成2,而另一個執行緒在操作共享物件時,還認為共享物件是1,這樣就可能帶來程式錯誤。

3、對方的回答
他對我的回答還是不滿意,告訴我是因為cpu中間有快取。
這樣我意識到每個執行緒在操作共享物件時,可能操作的是cpu中的快取物件,而不是堆中的物件。這樣一個執行緒修改cpu中快取物件,沒有及時的重新整理到堆中;另一個執行緒也是在操作cpu中快取物件,也不是操作堆中物件。

執行緒池相關

1、問題描述
假設執行緒池最大執行緒數為4,核心執行緒數為2,任務佇列為3;假設第3個執行緒進來的時候,前倆個執行緒任務都沒有完成。此時,新進來的第三個任務時,執行緒池會發生什麼?

2、問題分析
其實這道題目就在問執行緒池的工作流程。我回答的很不對,他就立馬問我在專案中是否用到執行緒池過。

唉,用是用過,但自己沒思考那麼仔細

3、問題答案

  • 第一個任務直接建立新執行緒執行;
  • 第二個任務也是直接建立新執行緒執行;
  • 第三個任務是放在佇列中,等到前倆個任務執行緒完成後才去執行

(1)總結一下執行緒池的工作流程:當一個任務提交的時候

  • 如果執行緒池中執行緒數小於核心執行緒數,就會建立一個執行緒來執行任務;
  • 如果執行緒池中執行緒數等於、大於核心執行緒數,就會把這個任務放在任務佇列中;
  • 如果任務佇列已經滿了的話,就執行拒絕策略

(2)從原始碼上分析:

// 以:ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,4,20,SECONDS,new ArrayBlockingQueue<>(3));為例  
// 核心過程是在 ThreadPoolExecutor的execute方法  

    /**
     * Executes the given task sometime in the future.  The task
     * may execute in a new thread or in an existing pooled thread.
     *
     * If the task cannot be submitted for execution, either because this
     * executor has been shutdown or because its capacity has been reached,
     * the task is handled by the current {@code RejectedExecutionHandler}.
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of
     *         {@code RejectedExecutionHandler}, if the task
     *         cannot be accepted for execution
     * @throws NullPointerException if {@code command} is null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        // 如果執行緒池數 < 核心執行緒數
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 如果執行緒池數 >= 核心執行緒數:
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

(1)執行緒池數大於等於核心執行緒數,不一定就會放在佇列中
這裡會採用double check,在放入佇列之前,有一個執行緒正好完成任務的話,就用這個執行緒來執行了。

執行緒安全容器

1、問題描述
jdk1.8ConcurrentHashMap鎖加在哪裡?

2、問題答案
是在每個Entry上加鎖,也就是對Entry下面的連結串列或者紅黑樹加鎖。
他又問我具體的加鎖的情況:當一個執行緒在Entry01下的連結串列或者紅黑樹進行put操作時,其他執行緒如果也要這個Entry01的連結串列或者紅黑樹進行操作時,就會阻塞住,等待上個結束為止,他才能進行操作。

其實他問我ConcurrentHashMap很多問題,但我剛開始一直在說jdk7jdk8的區別。以至於後面我還問他上面問題是基於jdk8的把,他說現在都用jdk8,當然問jdk8。這也告誡我,除非下次別人問我ConcurrentHashMapjdk7jdk8的區別,否則我就直接說jdk8的情況就好了。

關於上鎖時機和情形,當時回答不是很好把。他說下週來公司面試之前,好好看看ConcurrentHashMap,到時候會好好問這個的~

用過哪些中介軟體

問我用過哪些中介軟體,我說對Redis比較熟。他就問我用Redis的哪些功能,我說用它做過主從同步。
他就讓我說說是怎麼做的?我說假設開三個Redis例項,對其中倆個例項使用slaveof+主節點的ip+埠。然後在應用中,凡是寫操作就向主節點寫,凡是讀操作就往從節點讀。
他又問我Redis主從同步是用什麼實現的?我懵了,就說不會。

後來查了一下,有全量同步和增量同步倆種方式。但我覺得他意思應該不是問這個把?大佬們,求解?

他問Redis事務有用過嗎?我說看過這方面的書,但在專案上沒使用過。他就沒問了。


其他

面試結束的時候,他問我有什麼問題要問的?我問了:像我平常使用Redis的其他功能比較少,那我應該怎麼學習它?

他告訴我:當你在專案中使用某個技術之前,你應該已經掌握這個技術的所有功能

他說很簡單,如果你不全面瞭解這個技術的功能,你怎麼知道在什麼應用場景下,使用什麼功能呢。比如說,先把Redis的功能全部都理解一遍,以後遇到實際應用問題時,你才知道選擇什麼功能來解決。