本站消息

站长简介/公众号


站长简介:逗比程序员,理工宅男,前每日优鲜python全栈开发工程师,利用周末时间开发出本站,欢迎关注我的微信公众号:程序员总部,程序员的家,探索程序员的人生之路!分享IT最新技术,关注行业最新动向,让你永不落伍。了解同行们的工资,生活工作中的酸甜苦辣,谋求程序员的最终出路!

  价值13000svip视频教程,java大神匠心打造,零基础java开发工程师视频教程全套,基础+进阶+项目实战,包含课件和源码

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2021-05(16)

2021-06(58)

2021-07(11)

2021-08(50)

2021-09(37)

Java面试:2021.05.17

发布于2021-05-29 21:11     阅读(718)     评论(0)     点赞(13)     收藏(3)


1、Java 的 io 模型?

IO模型:BIO、NIO、IO多路复用、AIO。

 

2、io 多路复用的实现有哪些?

select、poll、epoll。

1、select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

2、select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

 

3、为什么使用三层架构,具体说说怎么松耦合的,举例子?

1、表现层(Web层):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候他的所见所得。

2、业务逻辑层(Server层):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理。
3、数据访问层(dao层):该层所做事务直接操作数据库,针对数据的增添、删除、修改、更新、查找等。
(内聚:一个模块内各个元素彼此结合的紧密程度;耦合:一个软件结构内不同模块之间互连程度的度量。)

 

优点:
  1、开发人员可以只关注整个结构中的其中某一层;
  2、可以很容易的用新的实现来替换原有层次的实现;
  3、可以降低层与层之间的依赖;
  4、有利于标准化;
  5、利于各层逻辑的复用;
      6、扩展性强。不同层负责不同的层面,如PetShop可经过简单的配置实现Sqlserver和oracle之间的转换,当然写好了也可以实现B/S与C/S之间的转换;
      7、安全性高。用户端只能通过逻辑层来访问数据层,减少了入口点,把很多危险的系统功能都屏蔽了;
         8、项目结构更清楚,分工更明确,有利于后期的维护和升级。

 

缺点
  1、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成;
  2、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码;
        3、增加了代码量,增加了工作量。

图片

4、cas 为什么就比 synchronized 轻量,什么原因?

锁分类

乐观锁:读的时候不加锁,之后在写的时候才加锁。并且在写的时候,会比较当前值跟预期值是否一致,只有一致才会去执行写操作。乐观锁基本上都是由CAS来实现的;

悲观锁:读写的时候都加锁;

公平锁:锁的获取遵循先进先出的原则;

不公平锁:锁的获取遵循先进先出的原则。

 

JAVA中的锁分类

偏向锁:当只有一个线程使用某个对象的情况下,该对象可以记录使用它的线程信息,如果一直都是同一个线程使用该对象,那么整个过程就不会加锁。

轻量级锁:当有两个或两个以上线程访问同一个对象时,偏向锁就不行了,此时就需要使用轻量级锁。其实就是乐观锁。

重量级锁:如果并发访问的线程很多,并且每个线程都要锁很长时间。此时轻量级锁就会不断的自旋检查,造成CPU被占满。此时就应该使用重量级锁,重量级锁会将等待锁的线程转入阻塞状态。虽然需要用户态和内核态的切换,但是避免了死循环自旋,大大降低了cpu的使用。

 

在线程数相对较少的时候,CAS实现比较快,性能优于synchronized,当线程数多于8后,CAS实现明显开始下降,反而时间消耗高于synchronized;

以上结果表明,synchronized是java提供的又简单方便,性能优化又非常好的功能,建议大家常用;CAS的话,线程数大于一定数量的话,多个线程在

循环调用CAS接口,虽然不会让其他线程阻塞,但是这个时候竞争激烈,会导致CPU到达100%,同时比较耗时间,所以性能就不如synchronized了。

 

5、Java 内存模型,那怎么保证可见性?

JMM是内存模型规范在Java语言中的体现。JMM保证了在多核CPU多线程编程环境下,对共享变量读写的原子性、可见性和有序性。

 

一个线程来改变started的状态,另外一个线程不停地来检测started的状态,如果是true就输出系统启动,如果是false就输出系统未启动。那么当start-Thread线程将状态改成true后,check-Thread线程在执行时是否能立即“看到”这个变化呢?答案是不一定能立即看到。这边我做了很多测试,大多数情况下是能“感知”到started这个变量的变化的。但是偶尔会存在感知不到的情况。这个现象就是在多核CPU多线程编程环境下会出现的可见性问题。

 

Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程在工作内存中保存的值是主内存中值的副本,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。等到线程对变量操作完毕之后会将变量的最新值刷新回到主内存。

 

但是何时刷新这个最新值又是随机的。所以就有可能一个线程已经将一个共享变量更新了,但是还没刷新回主内存,那么这时其他对这个变量进行读写的线程就看不到这个最新值。(还有一种可能就是虽然修改线程已经将最新值刷新到主内存中去了,但是读线程的工作内存中副本的缓存值还没过期,那么读线程还是会使用这个副本值,而不是主内存中的最新值)这个就是多CPU多线程编程环境下的可见性问题。

 

JMM对可见性问题的保证

在多CPU多线程编程环境下,对共享变量的读写会出现可见性问题。但是幸好JMM提供了相应的技术手段来帮我们规避这些问题,可以让程序正确运行。JMM针对可见性问题,主要提供了如下手段:

  • volatile关键字

  • synchronized关键字

  • Lock锁

  • CAS操作(原子操作类)

volatile关键字

使用volatile关键字修饰一个变量可以保证变量的可见性。

 

使用volatile修饰一个共享变量可以达到如下的效果:

  • 一旦线程对这个共享变量的副本做了修改,会立马刷新最新值到主内存中去;

  • 一旦线程对这个共享变量的副本做了修改,其他线程中对这个共享变量拷贝的副本值会失效,其他线程如果需要对这个共享变量进行读写,必须重新从主内存中加载。

 

内存屏障(英语:Memory barrier),也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。大多数现代计算机为了提高性能而采取乱序执行,这使得内存屏障成为必须。

 

语义上,内存屏障之前的所有写操作都要写入内存;内存屏障之后的读操作都可以获得同步屏障之前的写操作的结果。因此,对于敏感的程序块,写操作之后、读操作之前可以插入内存屏障。

 

对内存屏障做下简单的总结:

  • 内存屏障是一个指令级别的同步点;

  • 内存屏障之前的写操作都必须立马刷新回主内存;

  • 内存屏障之后的读操作都必须从主内存中读取最新值;

  • 在有内存屏障的地方,会禁止指令重排序,即屏障下面的代码不能跟屏障上面的代码交换执行顺序,即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。

 

synchronized关键字

使用synchronized代码块或者synchronized方法也可以保证共享变量的可见性。

 

当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。我们发现锁具有和volatile一致的内存语义,所以使用synchronized也可以实现共享变量的可见性。

 

Lock接口

使用Lock相关的实现类也可以保证共享变量的可见性。其实现原理和synchronized的实现原理类似,这边也就不再赘述了。

 

CAS机制(Atomic类)

使用原子操作类也可以保证共享变量操作的可见性。

 

原子操作类底层使用的是CAS机制。Java中CAS机制每次都会从主内存中获取最新值进行compare,比较一致之后才会将新值set到主内存中去。而且这个整个操作是一个原子操作。所以CAS操作每次拿到的都是主内存中的最新值,每次set的值也会立即写到主内存中。

 

 

今日份其他面试题:

1、如何判断输出的内容是单例的?

2、在线聊天功能怎么实现的?

3、synchronized 怎么调用到操作系统的?具体说说,操作系统消耗什么资源呢?

4、加锁为什么就可以保证内存屏障?

5、内存屏障是什么?具体说说吧?内存屏障为什么保障可见性?指令重排序?happen-before 原则了解么?

6、线程这边在操作系统怎么体现的,解决什么问题?在单核 cpu 中线程起什么作用?为什么轻量?

7、单核 CPU 线程解决问题,多核 CPU 中解决什么问题?

8、怎么设置 CPU 最佳线程数?

9、操作系统内存管理?分页?置换算法?有去深入了解吗?

10、线程池聊聊?怎么设置线程数,什么时候最优,为什么这么设置?



所属网站分类: 技术文章 > 博客

作者:niceboty

链接:http://www.javaheidong.com/blog/article/207276/18b3644d840c7186141a/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

13 0
收藏该文
已收藏

评论内容:(最多支持255个字符)