Interview [JAVA] the basis of multi-threaded


Contents of this sort is as follows:

java基础之线程池.png

1, the difference between processes and threads

Process is an executable program, the system is the basic unit of resource allocation; threads within a process is relatively independent executable unit is the basic unit of the operating system task scheduling.

2, inter-process communication

2.1, the operating system kernel buffer

Since the exchange of data between each process has its own memory space, the process required by the operating system kernel. Need to open up in an operating system kernel buffer, the process A will need to copy the data into a buffer, process B to read data from the buffer. Because the shared memory is not mutually exclusive access required to meet the semaphore exclusive access.

2.2, pipeline

Pipeline implementation:

  • Parent process to create a pipe, get two ends of the profile pointing pipe.
  • Father process fork a child process, the child process also has two profiles, with points at both ends of a pipe.
  • The parent process close the read end (fd (0)), the child process close the write end (fd (1)). Written inside the pipe to the parent process, the child process reads from the pipe inside.

Features pipeline:
only allows communication between processes with blood, allowing only one-way communication, process piping, process piping disappear disappear. Inner pipe is achieved by the circular queue.
Named Pipes (Named Pipes):
inter-process communication is achieved by way of the file. Allow inter-process communication unrelated

2.3, message queue

Composed by the message list, the presence system kernel. Overcome the semaphore less transmission information, only the carrier pipe plain character stream buffer of limited size and so on. Message distinguished by message types.

2.4, semaphores

Essentially a counter, not to transmit data for the purpose is mainly used to protect shared resources, making only one resource at a time exclusive process.

2.5 socket

The machine can be used for communication between different processes.
The socket comprising three attributes: domain, type, protocol.

  • Field includes ip port
  • It refers to two types of communication mechanisms: the flow (stream) and datagram (Datagram)
  • It refers protocol TCP / UDP underlying transport protocol

创建 socket 通过 bind 命名绑定端口,listen 创建队列保存未处理的客户端请求,accept 等待客户端的连接,connect 服务端连接客户端 socket,close 关闭服务端客户端的连接。

stream 和 datagram 的区别:
stream 能提供有序的、可靠的、双向的、基于连接的字节流(TCP),会有拆包粘包问题。
datagram 是无连接、不可靠、使用固定大小的缓冲区的数据报服务(UDP),因为基于数据报,且有固定的大小,所以不会有拆包粘包问题。

详细请参考:进程间的五种通信方式介绍

3、线程间的通信方式

共享内存:
Java 采用的就是共享内存,内存共享方式必须通过锁或者 CAS 技术来获取或者修改共享的变量,看起来比较简单,但是锁的使用难度比较大,业务复杂的话还有可能发生死锁。
消息传递:
Actor 模型即是一个异步的、非阻塞的消息传递机制。Akka 是对于 Java 的 Actor 模型库,用于构建高并发、分布式、可容错、事件驱动的基于 JVM 的应用。消息传递方式就是显示的通过发送消息来进行线程间通信,对于大型复杂的系统,可能优势更足。

详细请参考:Java 内存模型分析

4、多线程的优缺点

优点:
充分利用 cpu 的资源,提高 cpu 的使用率,使程序的运行效率提高。
缺点:
有大量的线程会影响性能,操作系统会在线程之间切换,会增加内存的开销。可能会产生死锁、存在线程之间的并发问题。

5、创建线程的方法

  1. 集成 Thread 类,重写 run 方法,利用 start 启动线程。
  2. 实现 Runable 接口创建线程,重写 run 方法,通过 new Thread 方式创建线程。
  3. 通过 callable 和 futuretask 创建线程,实现 callable 接口,重写 call 方法,使用 future 对象包装 callable 实例,通过 new Thread 方式创建线程。
  4. 通过线程池创建线程。

6、runable 和 callable 区别

  1. runable 是重写 run 方法,callable 重写 call 方法。
  2. runable 没有返回值,callable 有返回值。
  3. callable 中的 call 方法可以抛出异常,runable 中的 run 方法不能向外界抛出异常。
  4. 加入线程池运行 runable 使用 execute 运行,callable 使用 submit 方法。

7、sleep 和 wait 区别

  1. wait 只能在 synchronized 块中调用,属于对象级别的方法,sleep 不需要,属于 Thread 的方法。
  2. 调用 wait 方法时候会释放锁,sleep 不会释放锁。
  3. wait 超时之后线程进入就绪状态,等待获取 cpu 继续执行。

8、yield 和 join 区别

  1. yield 释放 cpu 资源,让线程进入就绪状态,属于 Thread 的静态方法,不会释放锁,只能使同优先级或更高优先级的线程有执行的机会。
  2. join 等待调用 join 方法的线程执行完成之后再继续执行。join 会释放锁和 cpu 的资源,底层是通过 wait 方法实现的。

9、死锁的产生条件

  1. 互斥条件。
  2. 请求与保持条件。
  3. 不可剥夺条件。
  4. 循环等待条件。

详细请参考:并发编程挑战:死锁与上下文切换

10、如何解决死锁

  1. 破坏请求与保持条件
    静态分配,每个线程开始前就获取需要的所有资源。
    动态分配,每个线程请求获取资源时本身不占有资源。
  2. 破坏不可剥夺条件
    当一个线程不能获取所有的资源时,进入等待状态,其已经获取的资源被隐式释放,重新加入到系统的资源列表中,可被其他线程使用。
  3. 死锁检测:银行家算法

11、threadLocal 的实现

  1. ThreadLocal 用于提供线程局部变量在多线程环境下可以保证各个线程里面的变量独立于其他线程里的变量。
  2. 底层使用 ThreadLocalMap 实现,每个线程都拥有自己的 ThreadLocalMap,内部为继承了 WeakReference 的 Entry 数组,包含的 Key 为 ThreadLocal,值为 Object。

详细请参考:【SharingObjects】ThreadLocal

12、threadLocal 什么时候会发生内存泄漏

java.lang.ThreadLocal.ThreadLocalMap.Entry:

 static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                //重点!!!!!
                super(k);
                value = v;
            }
        }

因为 ThreadLocalMap 中的 key 是弱引用,而 key 指向的对象是 threadLocal,一旦把 threadLocal 实例置为 null 之后,没有任何强引用的对象指向 threadLocal 对象,因此 threadLocal 对象会被 Gc 回收,但与之关联的 value 却不能被回收,只有当前线程结束后,对应的 map value 才会被回收。如果当前线程没结束,可能会导致内存泄漏。
如线程池的场景,在线程中将 threadlocal 置为 null,但线程没被销毁且一直不被使用,就可能会导致内存泄漏

在调用 get、set、remove 方法时,会清除线程 map 中所有 key 为 null 的 value。所以在不使用 threadLocal 时调用 remove 移除对应的对象。

13、线程池

13.1、线程池类结构

ThreadPoolExecutor 继承关系图:
ThreadPoolExecutor.png

13.2、shutDown 和 shutDownNow 的区别、

shutDown 方法执行之后会变成 SHUTDOWN 状态,无法接受新任务,随后等待已提交的任务执行完成。
shutDownNow 方法执行之后变成 STOP 状态,无法接受新任务。并对执行中的线程执行 Thread.interrupt()方法。

  • SHUTDOWN:不接受新任务提交,但是会继续处理等待队列中的任务。
  • STOP:不接受新任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。
13.3、线程池的参数
  1. CorePoolSize 核心线程数
  2. MaximumPoolSize 最大线程数,线程池允许创建的最大线程数
  3. keepAliveTime 空闲线程的存活时间
  4. wokeQueue 任务队列
  5. handler 饱和策略
  6. threadFactory 用于生成线程。

当任务来时,如果当前的线程数到达核心线程数,会将任务加入阻塞队列中,如果阻塞队列满了之后,会继续创建线程直到线程数量达到最大线程数,如果线程数量已经达到最大线程数量,且任务队列满了之后,会执行拒绝策略。

如果想让核心线程被回收,可以使用 allowCoreThreadTimeOut 参数,如果为 false(默认值),核心线程即使在空闲时也保持活动状态。如果 true,核心线程使用 keepAliveTime 来超时等待工作。

13.4、线程池的饱和策略
  1. CallerRunsPolicy:由提交任务的线程自己执行这个任。
  2. AbortPolicy (默认): 直接抛出 RejectExecutionException 异常。
  3. DisCardPolicy:不做处理,抛弃掉当前任务。
  4. DiscardOldestPolicy: 把队列队头的任务直接扔掉,提交当前任务进阻塞队列。
13.5、线程池分类

java.util.concurrent.Executors 类:

  1. newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

生成一个固定大小的线程池,此时核心线程数和最大线程数相等,keepAliveTime = 0 ,任务队列采取 LinkedBlockingQueue 无界队列(也可设置为有界队列)。
适用于为了满足资源管理需求,而需要限制当前线程数量的应用场景,比如负载比较重的服务器。

  1. newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));

生成只有一个线程的线程池,核心线程数与最大线程数都是 1,keepAliveTime = 0,任务队列采取 LinkedBlockingQueue,适用于需要保证顺序地执行各个任务,并且在任意时间点不会有多个线程是活动的应用场景。

  1. newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

核心线程数是 0,最大线程数是 int 最大值,keepaliveTime 为 60 秒,任务队列采取 SynchronousQueue,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器。

  1. newScheduledThreadPool
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

定长的线程池,支持周期性任务,最大线程数是 int 最大值,超时时间为 0,任务队列使用 DelayedWorkQueue,适用于需要多个后台执行周期任务,同时为了满足资源管理需求而需要限制后台线程的数量的应用场景。

13.6、任务执行过程中出现异常会怎么样?

任务执行失败后,只会影响到当前执行任务的线程,对于整个线程池是没有影响的。

详细请参考:ThreadPoolExecutor 线程池任务执行失败的时候会怎样

13.7、线程池的底层实现
  1. 使用 hashSet 存储 worker
  2. 每个 woker 控制自己的状态
  3. 执行完任务之后循环获取任务队列中的任务
13.8、重启服务、如何优雅停机关闭线程池

kill -9 pid 操作系统内核级别强行杀死某个进程。
kill -15 pid 发送一个通知,告知应用主动关闭。

ApplicationContext 接受到通知之后,会执行 DisposableBean 中的 destroy 方法。
一般我们在 destroy 方法中做一些善后逻辑。
调用 shutdown 方法,进行关闭。

13.9、为什么使用线程池
  1. 降低资源消耗,减少创建销毁线程的成本。
  2. 提高响应速度。
  3. 提高线程的可管理性,线程的无限制的创建,消耗系统资源,降低系统的稳定性。

Guess you like

Origin www.cnblogs.com/clawhub/p/12057771.html