写代码啦
记录一次电话面试
回复数(4) 浏览数(689)
皮的上头 04月16日 22:12 最后回复来自: 青青 Java 笔试面试
{{topic.upvote_count || 0}} 编辑 回复

线程安全问题

1、问题描述
为什么java有多线程安全问题?JavaScriptRedis却没有线程安全问题?

2、我的回答
因为java中允许多个线程可以共同操作某个对象。
在线程模型中,多个线程可以操作共享对象,假设一个线程A将共享对象从1变成2,而另一个线程在操作共享对象时,还认为共享对象是1,这样就可能带来程序错误。

3、对方的回答
他对我的回答还是不满意,告诉我是因为cpu中间有缓存。
这样我意识到每个线程在操作共享对象时,可能操作的是cpu中的缓存对象,而不是堆中的对象。这样一个线程修改cpu中缓存对象,没有及时的刷新到堆中;另一个线程也是在操作cpu中缓存对象,也不是操作堆中对象。

也不知道这样理解对不对~

线程池相关

1、问题描述
假设线程池最大线程数为4,核心线程数为2,任务队列为3;假设第3个线程进来的时候,前俩个线程任务都没有完成。此时,新进来的第三个任务时,线程池会发生什么?

2、问题分析
其实这道题目就在问线程池的工作流程。我回答的很不对,他就立马问我在项目中是否用到线程池过。

唉,用是用过,但自己没思考那么仔细

3、问题答案
- 第一个任务直接创建新线程执行;
- 第二个任务也是直接创建新线程执行;
- 第三个任务是放在队列中,等到前俩个任务线程完成后才去执行

(1)总结一下线程池的工作流程:当一个任务提交的时候
- 如果线程池中线程数小于核心线程数,就会创建一个线程来执行任务;
- 如果线程池中线程数等于、大于核心线程数,就会把这个任务放在任务队列中;
- 如果任务队列已经满了的话,就执行拒绝策略
thread_pool_work_flow.png thread_pool_work_flow.png

(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的功能全部都理解一遍,以后遇到实际应用问题时,你才知道选择什么功能来解决。


第一次写面试记录贴,有些细节都没写全,有些可能表述的不是那么准确。~_~

{{topic.upvote_count || 0}}

线程安全问题

1、问题描述
为什么java有多线程安全问题?JavaScriptRedis却没有线程安全问题?

2、我的回答
因为java中允许多个线程可以共同操作某个对象。
在线程模型中,多个线程可以操作共享对象,假设一个线程A将共享对象从1变成2,而另一个线程在操作共享对象时,还认为共享对象是1,这样就可能带来程序错误。

3、对方的回答
他对我的回答还是不满意,告诉我是因为cpu中间有缓存。
这样我意识到每个线程在操作共享对象时,可能操作的是cpu中的缓存对象,而不是堆中的对象。这样一个线程修改cpu中缓存对象,没有及时的刷新到堆中;另一个线程也是在操作cpu中缓存对象,也不是操作堆中对象。

也不知道这样理解对不对~

线程池相关

1、问题描述
假设线程池最大线程数为4,核心线程数为2,任务队列为3;假设第3个线程进来的时候,前俩个线程任务都没有完成。此时,新进来的第三个任务时,线程池会发生什么?

2、问题分析
其实这道题目就在问线程池的工作流程。我回答的很不对,他就立马问我在项目中是否用到线程池过。

唉,用是用过,但自己没思考那么仔细

3、问题答案
- 第一个任务直接创建新线程执行;
- 第二个任务也是直接创建新线程执行;
- 第三个任务是放在队列中,等到前俩个任务线程完成后才去执行

(1)总结一下线程池的工作流程:当一个任务提交的时候
- 如果线程池中线程数小于核心线程数,就会创建一个线程来执行任务;
- 如果线程池中线程数等于、大于核心线程数,就会把这个任务放在任务队列中;
- 如果任务队列已经满了的话,就执行拒绝策略
thread_pool_work_flow.png thread_pool_work_flow.png

(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的功能全部都理解一遍,以后遇到实际应用问题时,你才知道选择什么功能来解决。


第一次写面试记录贴,有些细节都没写全,有些可能表述的不是那么准确。~_~

689
回复 编辑