《面试笔记》

7:docker的优点

容器化,在docker环境中,要复制、启动、关闭、删除一个容器很简单,一行命令就可以了,端口可以随便自己开

兼容性,不需要再去考虑操作系统、依赖环境的影响

移植性,docker容器的导入导出都很方便,从jdk、tomcat、数据库的安装、数据的迁移、nginx的迁移、配置等等都要重新搞一个,但docker的迁移是容器的迁移,只需要在目标迁移的机器上安装docker,然后把相关的docker容器导入进去,重启启动,然后把网络安全搞一搞(如端口之类的),就完成迁移了。

8:从dockr里下载一些镜像用什么命令

镜像是运行容器的前提,我们可以使用 docker pull [IMAGE_NAME]:[TAG]命令来下载镜像,其中 IMAGE_NAME 表示的是镜像的名称,而 TAG 是镜像的标签,也就是说我们需要通过 “镜像 + 标签” 的方式来下载镜像。

9:docker created的作用

创建一个新的容器但不启动它

10:docker start和run的区别

docker run :创建一个新的容器并运行一个命令,是将镜像放入容器并启动容器。 docker run一般需要跟几个参数,如下所示:

docker run -it --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
-i: 以交互模式运行容器,通常与 -t 同时使用;
-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
–name: 为容器指定一个名称;
-p: 指定端口映射,格式为:主机(宿主)端口:容器端口;
-e: 设置环境变量;
-d:运行的镜像名,并返回容器ID;

docker start :启动一个或多个已经被停止的容器。类似用法的还有docker stop和docker restart

启动已被停止的容器mysql-test

docker start mysql-test

停止运行中的容器mysql-test

docker stop mysql-test

重启容器mysql-test

docker restart mysql-test

除了使用容器名,docker start/stop/restart还可以使用容器ID进行操作。

11:docker的网络模式

四类网络模式:

选项 描述 Host 基础镜像(容器不会虚拟出自己的网卡,配置主机的IP等,而是使用宿主机的IP和端口; Container 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP,端口范围; None 该模式关闭了网络功能 Bridge 默认为该模式,此模式会为每一个容器分配,设置IP等

并将容器连接到一个docker0的虚拟网桥,通过docker0网桥以及iptables nat表配置与宿主机通信。

12:虚拟机的网络模式

1、桥接模式(Bridged)直接连接物理网络;就好像在局域网中添加了一台新的、独立的计算机一样。因此,虚拟机也会占用局域网中的一个IP地址

2、网络地址转换模式(NAT)用于共享主机的IP地址

3、主机模式(Host-only)与主机共享的专用网络

13:docker之间是怎么通信的

比如我们构建一个SpringBoot项目的容器和一个Redis的容器,我们希望SpringBoot项目可以正常访问到Redis容器,通常做法是这样的:

假如我们的服务器公网IP地址是178.78.7.8,然后我们在服务器上部署了2个Docker服务,分别是SpringBoot服务和Redis服务,一般我们图方便直接将SpringBoot服务的配置文件Redis连接地址填写为公网IP地址,例:spring.redis.host=172.17.0.4

如果我们的SpringBoot服务和Redis服务都安装在宿主机上面,就不用绕一圈公网来访问Redis

通过本地127.0.0.1来访问Redis就完全不用担心网络因素而影响通信,且本地通信速度更佳所以没必要再访问公网IP绕一圈。

14:自定义线程池的核心线程数和最大线程数如何设置

线程池的最大核心线程数到底如何设置(cpu密集型,IO密集型)

*CPU密集型:根据cpu的核数(是几核的就定义为几)可以保持CPU的效率最高!*

如何获取cpu核数:

方式1、查看我们任务管理器,查看性能,cpu(12核)

IO密集型:判断我们程序中十分耗IO的线程。(只要大于这个数就ok了****

核心线程: CPU密集型:核心线程数=CPU核心数(或 核心线程数=CPU核心数+1)。 I/O密集型:核心线程数=2*CPU核心数(或 核心线程数=CPU核心数/(1-阻塞系数))。

最大线程: CPU密集型应用,最大线程设置为 N+1。 IO密集型经验应用,最大线程设置为 2N+1 (N为CPU数量,下同)。

15:synchronized和Volatile的一个区别是什么

1,作用的位置不同 synchronized是修饰方法,代码块。 volatile是修饰变量。

2,作用不同 synchronized,可以保证变量修改的可见性及原子性,可能会造成线程的阻塞;synchronized在锁释放的时候会将数据写入主内存,保证可见性; volatile仅能实现变量修改的可见性,但无法保证原子性,不会造成线程的阻塞;volatile修饰变量后,每次读取都是去主内存进行读取,保证可见

16:wait和sleep的区别

  • sleep是Thread类的方法,wait是Object类中定义的方法**

  • sleep()方法可以在任何地方使用

  • wait()方法只能在synchronized方法或synchronized块中使用

1:wait让线程处于等待状态,会释放锁资源(其他线程可以执行)---notify进行唤醒

2:sleep让线程处于睡眠状态,不会释放锁资源

notify随机唤醒某一个和notifyAll唤醒所有,重新去抢占资源

17:Volatile实现内存可见性原理:

写操作时,通过在写操作指令后加入一条store屏障指令,让本地内存中变量的值能够刷新到主内存中

读操作时,通过在读操作前加入一条load屏障指令,及时读取到变量在主内存的值

18:解决可见性问题方案

同步方式解决可见性问题

while (flag) {
            synchronized (this) {
            }
        }

线程解锁前(退出同步代码块时):必须把自己工作内存中共享变量的最新值刷新到主内存中

线程加锁时(进入同步代码块时):将清空本地内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(加锁与解锁是同一把锁)

19:Sync锁升级机制

为了提高Synchronized的执行效率,Jvm设计了Synchronized的锁升级机制。既是根据需要修改锁状态,逐渐升级锁的类型。①无锁----->偏向锁------>轻量锁------->重量锁。锁升级过程是不可逆的,一旦升级则不会再发生回退。

synchronized的升级过程,

偏向锁未启动,创建出来的是普通对象, 如果有一个线程来抢占锁,该锁偏向此线程,这时升级为偏向锁。 在偏向锁的基础上又来一个线程抢占锁此时升级为轻量级锁 当一个线程没有抢占到锁,并且自旋了一定时间后还没有抢到锁,就会升级成重量级锁。 在偏向锁的基础上如果出现了重度竞争就会直接升级成重量级锁

20:为什么不能使用内置线程池:(由executors工具类提供)

实际开发中不允许使用executor去创建,而要通过ThreadPoolExecutor的方式

可能会出现OOM(内存溢出)

executor创建的线程会出现两种情况:

一种是程数可以接近于无限的创建,就可能占满内存;还有就是排队的线程太多了,队列太多也可能占满线程

例如:single创建线程它的队列长度是Interger的最大值

21:为什么阿里不允许用Executors创建线程池,而是通过ThreadPoolExecutor的方式?

原因在于:(摘自阿里编码规约)

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Executors各个方法的弊端:

1)newFixedThreadPool和newSingleThreadExecutor:

主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。

2)newCachedThreadPool和newScheduledThreadPool:

主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

22:java集合类框架的基本接口有哪些?

Collection:代表一组对象,每一个对象都是它的子元素

Set:不包括重复元素的Collection

List:有顺序的Collection,并且可以包含重复元素

Map:可以把键(key)映射到值(value)的对象,键不能重复

List接口常用方法:

add向列表的尾部添加指定的元素。

add(int index, Object element): 在列表的指定位置插入指定元素

size()返回列表中的元素个数

get(int index): 返回列表中指定位置的元素,index从0开始

set(int i, Object element): 将索引i位置元素替换为元素element并返回被替换的元素。

remove(int index): 移除列表中指定位置的元素,并返回被删元素。

isEmpty(): 判断列表是否包含元素,不包含元素则返回 true,否则返回false。

set接口常用的方法

添加元素可以使用 add() 方法:

添加了两次,它在集合中也只会出现一次,因为集合中的每个元素都必须是唯一的。

contains() 方法来判断元素是否存在于集合当中:

remove() 方法来删除集合中的元素:

clean删除所有

如果要计算 HashSet 中的元素数量可以使用 size() 方法:

迭代 HashSet

  • Set接口不能使用普通for循环进行迭代

增强for

可以使用 for-each 来迭代 HashSet 中的元素。

map接口常用的方法

put:添加 remove:根据键删除映射关系 get:根据键获取值 size:获取元素个数 isEmpty:判断个数是否为0 clear:清空 containKey:查找键是否存在

23:List集合是线程不安全的,你是怎么使用List集合的呢?

使用Collections集合工具类,对集合进行同步处理: List<String> list = Collections.synchronizedList(new ArrayList<>()); 但是在多线程开发中,对其进行遍历,需要添加 synchronized 关键字,因为List的 add、index 等方法中都是带有synchronized 关键字,但是在 iterator 中没有synchronized 关键字。

24:Conllection和Conllections的区别是什么?

Conllection是一个单列集合的接口,Collections是一个操作集合的工作类,两者本质就不一样

25:hashmap1.7出现环链的原因

hashmap是线程不安全的,当两个线程同时进入到它的扩容方法,这时候如果一个线程阻塞了,另一个线程正常执行下去,这个时候阻塞的线程恢复了,再用头插法去操作会导致两个元素的下一个节点指向对方,就造成了环链

26:什么是HashMap双链循环/死锁?

在多线程扩容的情况下,一个线程执行到一半,还未扩容,而另一个线程却抢走先行扩容了,这时候可能出现第一个线程的元素与第二个线程中的元素相互引用的情况,相互引用就会造成死锁。

JDK1.8版本避免了双链循环,但不是完全避免,看过一些测试文章,红黑树之间也可能出现死循环,只是比较1.7版本,几率降低。

27:Hashtable跟HashMap有什么区别?

HashMap是线程不安全的(多线程环境下会出问题);

Hashtable是线程安全的(但效率低下);

Hashtable底层和哈希表一样,扩容因子是0.75,扩容倍率是2倍。

Hashtable一次只能执行一个线程(全表加锁),采取悲观锁(增善改的方法上都加了synchronized)保证了线程安全。

Hashtable不允许null值(key和value都不允许),HashMap允许null值(key和value都允许)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

admiraldeworm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值