3/12

1.四次挥手谁先发起
全双工,哪边先挥手都一样,一样的关闭流程。
http1.0版本三次握手建立连接后,传送好数据以后就是由服务端发起关闭连接
2.如何保证数据包传向正确的目的地
*应用层(应用程序之间的接口服务)HTTP FTP DNS…等协议
表示层(数据的加密压缩,解压缩,解密等操作可以确保被另一个系统的应用层读取)
会话层(建立主机和主机之间通信连接,保证传输不中断)
*传输层(主机到主机之间传输 )TCP UDP协议
*网络层(寻址,路由选择)
*数据链路层(帧 为了弥补物理层的不足,在线路上提供无差错的数据传输,进行纠错检错 流量控制等)
*物理层(比特流传输,物理连接 电线,电缆连接器这种的建立维护断开)
3.unix如何创建子进程?
fork创建子进程分配内存,并完全复制父进程代码:状态信息程序计数器等等。
一般我们会和exec()连用:调用另一个进程,并将fork开辟出的内存的那块代码完全被新代码代替。 前面的拷贝就是白费力气 copy
所以有了vfork share
vfork出的子进程一开始和父进程共享地址空间,霸占他的房子(父进程相当于阻塞了,只能这个子进程exec才去创建自己的一块内存或者exit了,才轮到父进程)
在子进程中return,那么基本是下面的过程:
1)子进程的main() 函数 return了,于是程序的函数栈发生了变化。
2)而main()函数return后,通常会调用 exit()
3)这时,父进程收到子进程exit(),开始从vfork返回,但是尼玛,老子的栈都被你子进程给return干废掉了,你让我怎么执行?
所以很危险
用CopyOnWrite(COW)技术创建子进程
fork基础上只在写的时候拷贝
4.什么是僵尸进程,如何解决
首先我们先讲讲wait/waitpid
unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态等)。直到父进程通过wait / waitpid来取时才释放。 但这样就导致了问题,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构(任何哦都会有僵尸结构),等待父进程处理。这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理,成为孤儿进程。(孤儿进程没有危害)
5.AQS公平非公平如何实现;
volatile定义的state字段,通过控制对state状态的获取与释放来做到线程之间的同步。而且AQS里面又维护了一个队列,可以实现线程排队机制,也可以做等待唤醒操作。队列里的每一个节点都是有状态的,分别为CANCELLED、SIGNAL、CONDITION、PROPAGATE,它是通过一个valatile定义的waitStatus来判断节点是处于上述4个状态中的哪一个来分别作出响应。
比如吧waitStatus=1,处于CANCELLED状态
在这里插入图片描述
基于AQS的锁(比如ReentrantLock)原理大体是这样:
有一个state变量,初始值为0,假设当前线程为A,每当A获取一次锁,state++. 释放一次,state–.锁会记录当前持有的线程。
当A线程拥有锁的时候,state>0. B线程尝试获取锁的时候会对这个state有一个**CAS(0,1)**的操作,尝试几次失败后就挂起线程,进入一个等待队列。
如果A线程恰好释放,–state==0, A线程会去唤醒等待队列中第一个线程,即刚刚进入等待队列的B线程,B线程被唤醒之后回去检查这个state的值,尝试CAS(0,1),而如果这时恰好C线程也尝试去争抢这把锁
B线程先被唤醒,,,,然后再尝试CAS

非公平锁实现
C直接尝试对这个state CAS(0,1)操作,并成功改变了state的值,B线程获取锁失败,再次挂起,这就是非公平锁,B在C之前尝试获取锁,而最终是C抢到了锁。
公平锁
C发现有线程在等待队列,直接将自己进入等待队列并挂起,B获取锁
6.不同情况下int、Integer的==、equals产生的结果;
1.Integer是一个引用类型的变量默认null,而int是一个基本类型的变量默认0。对于基本类型变量,比较的是其值。。。。。 而对于引用类型的变量,==比较地址,equals比的是值。

2.Integer i4 = 127 指向常量池,因为Java在编译的时候,Integer i4=127被翻译成-> Integer i4= Integer.valueOf(127); 对于-128到127之间的数,都是符合的结论。有区间限定哦。。。。

3.Integer i9 = new Integer(127); 指向堆 只要两个都是new出来的,==比较都为false。若要比较值是否相等,需使用equals方法进行比较。

4.int和Integer(无论new否)比==/equals,都为true,因为会把Integer自动拆箱为int再去比。
7. TCP的拥塞控制?
慢开始 拥塞避免 快重传 快恢复
纵坐标是拥塞窗口。
超时重传and快重传的区别
超时重传会有一个定时器,超过那个时间还没有收到回复,才会重发
快重传超过三次得不到响应,就重传
超时重传后,ssthresh=0.5cwnd,从0重新开始慢开始
如果是快重传,ssthresh=0.5cwnd,从cwnd=ssthresh开始拥塞避免(斜线那个)
8. UDP建不建连接是指的什么?
发送者知道接收者的IP和端口就可以开始发送。至于是不是接收端收到,就要看你这个发送者是不是一直监控发送时在发送端的端口。

物理连接所对应,物理连接是实实在在存在的,看得见摸得着,比如索道。而虚拟连接(TCP)是不存在的,通过双向的消息、消息确认(超时重传,拥塞控制等等)来模拟物理连接。
UDP不管你这些就一直传送东西
9. Java里有几种锁?
1公平/非公平:
ReentrantLock而言,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大,可能会造成饥饿。对于Synchronized而言,也是一种非公平锁
2可重入锁/不可重入锁
ReentrantLock和Synchronized都是可重入锁,可重入锁指的是可重复可递归调用的锁,在外层方法使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。
public sychrnozied void test() {
xxxxxx;
test2();
}
public sychronized void test2() {
yyyyy;
}
在上面代码段中,执行 test 方法需要获得当前对象作为监视器的对象锁,但方法中又调用了 test2 的同步方法。
如果锁是具有可重入性的话,那么该线程在调用 test2 时并不需要再次获得当前对象的锁,可以直接进入 test2 方法进行操作。
如果锁是不具有可重入性的话,那么该线程在调用 test2 前会等待当前对象锁的释放,实际上该对象锁已被当前线程所持有,不可能再次获得。
如果锁是不具有可重入性特点的话,那么线程在调用同步方法、含有锁的方法时就会产生死锁。
3.独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。共享锁是指该锁可被多个线程所持有。
但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
4.互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock
5.乐观锁/悲观锁
乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
6.分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。jdk1.7ConcurrentHashMap
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。
7.偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
8.自旋锁
缺点是循环会消耗CPU。
10. 介绍一下java异常体系?
编译时异常(使用try…catch…finally或者throws),运行时异常(我们应该修正代码)。
在这里插入图片描述
11线程池总体补充:
1.线程池就是事先将线程放到一个容器中,当使用线程的时候,不用再去new出一个线程,直接从线程池取出来就可以了
2.通过execute方法去执行线程
在这里插入图片描述
3.ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor几个之间的关系
a.Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的(上边的代码也显示了通过把任务放在execute里面)
b.然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
c.抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
d.然后ThreadPoolExecutor继承了类AbstractExecutorService。
在ThreadPoolExecutor类中有几个非常重要的方法:
execute()
submit()
shutdown()
shutdownNow()
在ThreadPoolExecutor类中提供了四个构造方法(那些参数的组合嘛)
源码就是线程池来了一个任务后怎么执行的那个过程
4.可调度线程池:指定延时后执行任务,周期性重复执行任务。
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor并实现
具体那些接口呢?————继承上边的线程池,又弄了个调度接口
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService {
//……
}
内部源码的构造器看一下(都涉及到了延时)
public interface ScheduledExecutorService extends ExecutorService {

//指定时延后调度执行任务
public ScheduledFuture<?> schedule(Runnable command,
                                   long delay, TimeUnit unit);

//指定时延后调度执行任务
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                       long delay, TimeUnit unit);

//指定时延后开始执行任务,以后每隔period的时长再次执行该任务
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                              long initialDelay,
                                              long period,
                                              TimeUnit unit);

在这里插入图片描述
//指定时延后开始执行任务,以后任务执行完成后等待delay时长,再次执行任务
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
在这里插入图片描述
而任务队列是一个延时阻塞队列,所以也就造成了周期性运行的假象
源码要求:如果delay小于等于0,那么就是说需要被立即调度,否则延时delay这样一段时间。也就是延时消费
一个任务会被重复添加到一个延时任务队列,所以同一时间任务队列中会有多个任务待调度,线程池会首先获取优先级高的任务执行。如果我们的任务运行时间大于设置的调度时间 ,那么效果就是任务运行多长时间,调度时间就会变为多久,因为添加到任务队列的任务的延时时间每次都是负数,所以会被立刻执行。
12.python和java有啥不同?
java更适合商业开发,python适合数据分析
python四种数据:整数,长整数、浮点数和复数。 java八种:整数4(包括byte),浮点2,字符char,bool类型.。。。java中String不算基本类型!
Java编译以后才能运行,Python直接就可以运行;
JAVA 里的块用大括号对包括,Python 以冒号 + 缩进表示。
JAVA 的类型要声明——静态语言,Python 的类型不需要—动态语言
JAVA 每行语句以分号结束,Python 可以不写分号。
Java中char表示单个字符,String表示一个字符串双引号“ ”表示,python中没有char概念,用单引号‘ ’或双引号“ ”来表示。
13.阻塞队列怎么用?是线程安全的么?
在这里插入图片描述
出队列,入队列(add put take。。。。。等等方法)
put:如果发现队列使用的数组没有可用的容量了,那么就等待在一个条件变量上,而这个条件变量需要在有空闲空间的时候唤醒等待在他上面的线程。
offer:该方法在插入操作不能立即执行的时候就会返回false,否则会返回true代表插入成功了。ReentrantLock插入操作需要锁住,也就是靠锁来达到同步线程的功能
take:取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入;
poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null;
poll(long timeout, TimeUnit unit):从BlockingQueue取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则直到时间超过,返回失败
PriorityBlockingQueue是一个优先阻塞队列。所谓优先队列,就是每次从队队列里面获取到的都是队列中优先级最高的,对于优先级,PriorityBlockingQueue需要你为插入其中的元素类型提供一个Comparator,PriorityBlockingQueue使用这个Comparator来确定元素之间的优先级关系堆这种数据结构的细节
ArrayBlockingQueue和LinkedBlockingQueue都是线程安全的,其他我感觉也是的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值