细节决定成败(一)
-
-
- **ScheduledThreadPool 执行周期性任务,假设某次出现了异常,周期任务还会继续吗?**
- **实现runnable是不是可以说明使用了组合的方式要比继承的方式要好?**
- **synchronized同步代码块为什么会有两个monitorexit?**
- **synchronized同步方法是什么原理?**
- ReentrantLock的tryLock()是否会有公平行为?
- ReentrantLock设置为公平锁有什么优缺点?
- ReentrantLock设置为非公平锁有什么优缺点?
- Kafka如何设计达到高吞吐量、低延迟?
- Spring框架中IOC容器有哪几种?
- Spring启动IOC容器的三种方式
- 关于Java8中提供的四个核心函数式接口的描述?
- Java Enum类中为什么构造函数必须是私有的?
- 下面哪种权限是同一包可以访问,不同包的子类可以访问,不同包的非子类不可以访问?
- Maven的仓库分为哪两大类
- Spring框架中有哪些不同类型的事件?
- JDBC连接池实现方式?
- SVN
- 存储过程跟动态sql相比有如下优点:
- MyBatis什么情况用注解,什么情况用xml绑定?
- 在使用Git进行开发时,发现图片文件image.png发生冲突,那么如何查看别人的版本?()
- maven中,对于一个多模块项目,管理项目依赖的版本是:
- 使用Git时,如果已经将改变的文件加入暂存区,但是突然又不想提交其中的service.java文件,那么该如何操作?
- 对于使用 upstart 的系统而言(Ubuntu 14.04、Debian 7 Wheezy),编辑哪个文件,在其中的 DOCKER_OPTS 中配置加速器地址?
- 对于使用 systemd 的系统(Ubuntu 16.04+、Debian 8+、CentOS 7),编辑哪个文件,在其中的 DOCKER_OPTS 中配置加速器地址?
- 要把镜像goldberg重新命名为mysqlberg
- swarm的调度模块的第一阶段,过滤器有几种?
- Docker用ADD指令复制文件时,下载后的文件权限自动设为?
- 在镜像的构建过程中 Docker 会遍历 Dockerfile 文件中的所有指令,异步执行。
- 阻塞与非阻塞队列
- 阻塞队列包含哪些常用的方法?add、offer、put 等方法的区别?
- SynchronousQueue的容量是多少?
- PriorityBlockingQueue 优先级队列
- DelayQueue 延迟队列
- StringBuilder原理
- JVM内部采用了StringBuffer来连接字符串了,我们为什么不直接用“+”来连接字符串?
-
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
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 文件中的所有指令,异步执行。
错误,同步执行
阻塞与非阻塞队列
阻塞队列 | 非阻塞队列 |
---|---|
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,在判断一下,容量是否足够,不够就直接扩容到需要的容量大小。
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分别指代的意义
- 表示月份是大写的M;
- 表示分钟则是小写的m;
- 24小时制的是大写的H;
- 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,同样也会有线程安全问题。