Bootstrap

面试题总结(七)

目录

1、1000个多并发线程,10台机器,每台机器4核的,设计线程池大小。

2、数据库如何加快数据查询

3、网络传输中的分包、粘包与丢包

4、 ArrayList 和 LinkedList 遍历操作效率比较?   

5、什么是内部类?内部类的作用

 6、synchronized与static synchronized 的区别:      

7、如何控制某个方法允许并发访问线程的个数?

 8、什么导致线程阻塞?

9、两个进程同时要求写或者读,能不能实现?如何防止进程的同步?

10、java线程系列之线程模型

11、Sql语句各参数的执行顺序

 12、一般在什么字段上建索引

13、如何从一张表中查出name字段不包含“XYZ”的所有行?

14、MySQL行锁实现

15、何解决高并发减库存问题

16、给2万多名员工按年龄排序应该选择哪个算法?

17、万亿级别的两个URL文件A和B,如何求出A和B的差集C

18、List a=new ArrayList()和 ArrayList a =new ArrayList()的区别?

19、mysql延时关联

20、TCP怎么缩小发送方窗口


1、1000个多并发线程,10台机器,每台机器4核的,设计线程池大小。

        JDK 线程池的执行流程。

 Tomcat 的执行流程是这样的:

         10 个机器,1000 个请求并发,平均每个服务承担 100 个请求。服务器是 4 核的配置。那么如果是 CPU 密集型的任务,我们应该尽量的减少上下文切换,所以核心线程数可以设置为 5,队列的长度可以设置为 100,最大线程数保持和核心线程数一致。

        如果是 IO 密集型的任务,我们可以适当的多分配一点核心线程数,更好的利用 CPU,所以核心线程数可以设置为 8,队列长度还是 100,最大线程池设置为 10。

        我们也可以从核心线程数等于 5 开始进行系统压测,通过压测结果的对比,从而确定最合适的设置。

        我觉得线程池的参数应该是随着系统流量的变化而变化的。所以,对于核心服务中的线程池,我们应该是通过线程池监控,做到提前预警。同时可以通过手段对线程池响应参数,比如核心线程数、队列长度进行动态修改。

2、数据库如何加快数据查询

  • 升级硬件 
  • 根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。
  • 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null。可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=0
  • 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
  • 应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num=10 or num=20。可以这样查询:select id from t where num=10union all select id from t where num=20。
  • 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
  • 尽量避免大事务操作,提高系统并发能力。
  • 分库分表
  • 缓存

3、网络传输中的分包、粘包与丢包

  • 分包 , 传输过程中对一个报文进拆分发送。比如传输一个字符串“aabbcc”,那么在接收方可能先收到aa 然后 bb 最后cc。​接收端处理最好的办法是按长度读取,比如在发送aabbcc之前 先发送 这个aabbcc的长度,这个长度可以定义为short类型 2个字节。那么接收端可以先判断是否已经接收到2个字节,如果接收到,那么读取这个值,然后根据这个值接着判读下一个内容aabbcc是否已经完全接收,如果接收长度不足,则继续等待接收,长度达到即可读取aabbcc了。
  • 粘包 所谓粘包,是指发送端发送的两个报文,在接收端被拼在一起。由于TCP是面向流的协议,报文与报文之间是没有分界符号的。在接收端,所有的数据都逻辑上拼在一起给你。举例来说,你分10次发送10个长度为10的报文,在接收端,你可能只收到一个长度为100的报文,而不会收到10个消息。为了解决这个问题,你必须在接收端有能力把这些报文分隔开来。如果消息长度总是固定的,这就比较容易,只要按长度取出即可。如果长度不固定,一般有两种方法解决:a)使用特征字节。例如:如果是聊天程序,发送的是普通文本,一些字符是绝对不可能出现在正文中的,你可以使用这些字符做分隔符隔离不同消息。我们可以使用'\0'做分隔,一般对于全文本传输是比较安全的。b) 在发送方发送正文前,先发送一个长度。例如你要发送2345字节的内容,你可以先发送一个2字节的长度给对方,然后再发正文。接收放只要收到这个长度信息,就可以正确的分包。需要注意的,到底用多少字节来发送长度是应该预先约定的,一般2字节就足够,不过你约定4字节也是可以的。还要注意的是,如果接收一次报文后,解包完毕还剩下一部分内容,这些内容应该留给下次报文分包使用,而不能扔掉。
  • 丢包一般都是由于对上面说的理解不足引起的,因为TCP本身是确保不丢包的。除非连接被断开。

4、 ArrayList 和 LinkedList 遍历操作效率比较?   

                ArrayList从原理上就是数据结构中的数组,也就是内存中一片连续的空间,这意味着,当我get(index)的时候,我可以根据数组的(首地址+偏移量)。操作系统可以预读加快遍历。LinkedList可以简单理解为数据结构中的链表(说简单理解,因为其实是双向循环链表),在内存中开辟的不是一段连续的空间,而是每个元素有一个[元素|下一元素地址]这样的内存结构。

5、什么是内部类?内部类的作用

        将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。

 6、synchronized与static synchronized 的区别      

  • synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”,类的两个不同实例就没有这种约束了。
  • 那么static synchronized恰好就是要控制类的所有实例的访问了,static synchronized是限制线程同时访问jvm中该类的所有实例同时访问对应的代码快。

7、如何控制某个方法允许并发访问线程的个数?

public class SemaphoreTest {
	static Semaphore semaphore = new Semaphore(5, true);
 
	public static void main(String[] args) {
		for (int i = 0; i < 100; i++) {
			new Thread(new Runnable() {
 
				@Override
				public void run() {
					test();
				}
			}).start();
		}
 
	}
 
	public static void test() {
		try {
			// 申请一个请求
			semaphore.acquire();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "进来了");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "走了");
		// 释放一个请求
		semaphore.release();
	}
}

 8、什么导致线程阻塞?

  • 线程进行了休眠:线程执行了Thread.sleep(int n)方法,线程放弃CPU,睡眠n毫秒,然后恢复运行。
  • 线程等待获取同步锁才能进行下一步操作:线程要执行一段同步代码,由于无法获得相关的同步锁,只好进入阻塞状态,等到获得了同步锁,才能恢复运行
  • 线程执行wait()进入阻塞状态:线程执行了一个对象的wait()方法,进入阻塞状态,只有等到其他线程执行了该对象的notify()或notifyAll()方法,才可能将其唤醒。
  • 等待相关资源:线程执行I/O操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态。

9、两个进程同时要求写或者读,能不能实现?如何防止进程的同步?

        可以进行同时读写,但为了保证数据的正确,必须要针对进程访问的共享临界区进行处理;两个进程不能同时进入临界区,否则会导致数据错乱。常见的处理方式有:信号量、管程、会合、分布式系统。

信号量

        信号量是一个计数器,它只支持2种操作:P操作(进入临界区)和V操作(退出临界区)。假设有信号量SV,则对它的P、V操作含义如下

  • P(SV),如果SV的值大于0,意味着可以进入临界区,就将它减1;如果SV的值为0,意味着别的进程正在访问临界区,则挂起当前进程的执行;
  • V(SV),当前进程退出临界区时,如果有其他进程因为等待SV而挂起,则唤醒之;如果没有,则将SV加1,之后再退出临界区。

管程

        提出原因:信号量机制不足,程序编写困难、易出错。定义:是一个特殊的模块;有一个名字;由关于共享资源的数据结构及在其上操作上的一组过程组成。进程只能通过调用管程中的过程间接访问管程中的数据结构。

  • 互斥:管程是互斥进入的,为了保证数据结构的数据完整性管程的互斥由编译器负责保证的,是一种语言机制
  • 同步:设置条件变量及等待唤醒操作以解决同步问题

10、java线程系列之线程模型

  • 多对一模型:多对一线程模型,又叫作用户级线程模型,即多个用户线程对应到同一个内核线程上,线程的创建、调度、同步的所有细节全部由进程的用户空间线程库来处理。

    • 优点:用户线程的很多操作对内核来说都是透明的,不需要用户态和内核态的频繁切换,使线程的创建、调度、同步等非常快;

    • 缺点:由于多个用户线程对应到同一个内核线程,如果其中一个用户线程阻塞,那么该其他用户线程也无法执行;内核并不知道用户态有哪些线程,无法像内核线程一样实现较完整的调度、优先级等;

  • 一对一模型:又叫作内核级线程模型,即一个用户线程对应一个内核线程,内核负责每个线程的调度,可以调度到其他处理器上面。

    • 优点:实现简单

    • 缺点:对用户线程的大部分操作都会映射到内核线程上,引起用户态和内核态的频繁切换;内核为每个线程都映射调度实体,如果系统出现大量线程,会对系统性能有影响;

  • 多对多模型:又叫作两级线程模型,它是博采众长之后的产物,充分吸收前两种线程模型的优点且尽量规避它们的缺点。在此模型下,用户线程与内核线程是多对多(m : n,通常m>=n)的映射模型。区别于多对一模型,多对多模型中的一个进程可以与多个内核线程关联,于是进程内的多个用户线程可以绑定不同的内核线程,这点和一对一模型相似;又区别于一对一模型,它的进程里的所有用户线程并不与内核线程一一绑定,而是可以动态绑定内核线程, 当某个内核线程因为其绑定的用户线程的阻塞操作被内核调度让出CPU时,其关联的进程中其余用户线程可以重新与其他内核线程绑定运行。

        Java使用的就是一对一线程模型

11、Sql语句各参数的执行顺序

 12、一般在什么字段上建索引

  • 表的主键和外键建立索引
  • 在order by 或者 group by 后边建立索引
  • 数据量超过300的应该建立索引
  • 经常与其他表进行连接的表的字段,应该在该字段上建立索引
  • 经常出现在where子句中的字段应该建立索引,特别是大表字段
  • 索引应该建立在选择性高的字段
  • 不应该在字段比较长的字段上建立索引,因为会消耗大量的空间
  • 对于经常频繁进行修改和插入的表应该少建立索引,因为在修改和插入之后,数据库会去维护索引,会消耗资源

13、如何从一张表中查出name字段不包含“XYZ”的所有行?

        使用not like和子查询。select * from user where name not like "%XYZ%";

select * from user where id not in(select id from user where name like "%XYZ%")

14、MySQL行锁实现

        InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。 InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

15、何解决高并发减库存问题

        如果使用第二种方案假设三个用户请求减库存操作,完全可以让三个请求进三个不同的锁去扣减各自的库存数,此时三人没有排队可以保证他们同时减库存,而又不影响库存总数的准确性,因为三个请求操作的是各自锁所维护的库存数。随着业务增长,库存总数的分割可以不断细分直到缩短响应时间到合理范围,而这个库存总数的分割很好的保证了不会遇到瓶颈。但是由于这种业务架构的设计,导致业务不得不变得复杂,可以看到我们在进入分布式锁之前有一个称为库存总数协调器的模块。

        首先我们把库存分割成多块后解决的首要问题便是如何让请求均匀的依次进入每一个分布式锁中进而操作当前锁所负责的库存数。

        库存协调器的逻辑完全看各位自己业务模型来决定,你可以用雪花算法均匀分布也可使用ip或者用户标识取余去覆盖到每一个锁,总之实现方式看业务情况来决定,当然了很大几率会出现有的库存块内的库存总数消耗完了但有的还剩余,所以库存协调器一定要考虑到这类情况及时将库存较多的库存块内的库存数分散给其他库存块,以达到多线程减库存的效果。

16、给2万多名员工按年龄排序应该选择哪个算法?

        遍历一遍找到最小值和最大值,建立相应个数的链表数组,遍历一遍员工,将其放入响应的数组,最后链接各个链表。

17、万亿级别的两个URL文件A和B,如何求出A和B的差集C

        Bit映射->hash分组->多文件读写效率->磁盘寻址以及应用层面对寻址的优化 

18、List a=new ArrayList()和 ArrayList a =new ArrayList()的区别?

  •   List a=new ArrayList() 是使用了多态 但是他的对象还是List 只能使用List和Arrylist 共有的方法
  • ArrayList a =new ArrayList() 是直接创建对象 可以使用所有的ArrayList的方法

19、mysql延时关联

select * from table where xxx limit a,b;

select * from table where id in (select id from table where xxx limit a,b);

        mysql有种sql优化方式,叫延时关联,即通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据,尤其在大分页查询的场景下,可以提高查询效率。

       在覆盖索引的场景下,第一条的执行逻辑是

  •  通过索引找到(a+b)条符合查询条件的记录id
  • 再通过(a+b)个id回表查询这(a+b)条记录
  • 最后按分页条件给用户返回b条记录

        而第二条SQL的执行逻辑则是

  • 通过索引找到(a+b)条符合查询条件的记录id
  • 按分页条件取b个记录id,然后回表查询这b条记录
  • 最后给用户返回b条记录

20、TCP怎么缩小发送方窗口

        快恢复,缩小为发生拥塞时窗口大小的一半

;