说一下线程的生命周期?
线程是一个动态执行的过程,它也有一个从产生到死亡的过程,有五种状态。
- 新建(new Thread)
- 当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。例如:Thread t1=new Thread();
- 就绪(runnable)
- 线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
- 运行(running)
- 线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
- 死亡(dead)
- 当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
- 自然终止:正常运行run()方法后终止
- 异常终止:调用stop()方法让一个线程终止运行
- 阻塞(blocked)
- 阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
- 等待阻塞 ( o.wait-> 等待对列:运行(running)的线程执行 o.wait()方法,JVM 会把该线程放入等待队列(waitting queue)中。
- 同步阻塞 (lock-> 锁池 ):运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池(lock pool)中。
- 其他阻塞 (sleep/join):运行(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入可运行(runnable)状态。
- 阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
sleep()和wait()有什么区别吗?
- sleep()方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间
- 因为sleep() 是static静态的方法,他不能改变对象的锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象。
- wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程。
简述基于TCP和UDP的Socket编程主要步骤
-
Java分别为TCP和UDP 两种通信协议提供了相应的Socket编程类,这些类存放在java.net包中。与TCP对应的是服务器的ServerSocket和客户端的Socket,与UDP对应的是DatagramSocket。
-
基于TCP创建的套接字可以叫做流套接字,服务器端相当于一个监听器,用来监听端口。 服务器与客服端之间的通讯都是输入输出流来实现的。基于UDP的套接字就是数据报套接字,• 两个都要先构造好相应的数据包。
-
基于TCP协议的Socket编程的主要步骤
- 服务器端(server)
- 构建一个ServerSocket实例,指定本地的端口。这个socket就是用来监听指定端口的连接请求的。
- 重复如下几个步骤:
- 调用socket的accept()方法来获得下面客户端的连接请求。通过accept()方法返回的socket实例,建立了一个和客户端的新连接
- 通过这个返回的socket实例获取InputStream和OutputStream,可以通过这两个stream来分别读和写数据
- 结束的时候调用socket实例的close()方法关闭socket连接
- 客户端(client)
- 构建Socket实例,通过指定的远程服务器地址和端口来建立连接
- 通过Socket实例包含的InputStream和OutputStream来进行数据的读写
- 操作结束后调用socket实例的close方法,关闭
- 服务器端(server)
-
基于UDP协议的Socket编程的主要步骤
- 服务器端(server)
- 构造DatagramSocket实例,指定本地端口。
- 通过DatagramSocket实例的receive方法接收DatagramPacket.DatagramPacket中间就包含了通信的内容
- 通过DatagramSocket的send和receive方法来收和发DatagramPacket
- 客户端(client)
- 构造DatagramSocket实例
- 通过DatagramSocket实例的send和receive方法发送DatagramPacket报文
- 结束后,调用DatagramSocket的close方法关闭
- 服务器端(server)
反射机制的优缺点
-
静态编译:在编译时确定类型,绑定对象,即通过。
-
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
-
优点:可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中,它的灵活性就表现的十分明显。
-
缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
HashMap和Hashtable的区别和联系
-
实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用
-
两者的主要区别如下
-
Hashtable是早期JDK提供的接口,HashMap是新版JDK提供的接口
-
Hashtable继承Dictionary类,HashMap实现Map接口
-
Hashtable线程安全,HashMap线程非安全
-
Hashtable不允许null值,HashMap允许null值
-
Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
-
初始容量大小和每次扩充容量大小的不同 Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
-
哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
-
hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。
-
Hashtable计算hash值,直接用key的hashCode(),而HashMap重新计算了key的hash值,Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。
-
字符流字节流联系区别;什么时候使用字节流和字符流?
- 字符流和字节流是流的一种划分,按处理照流的数据单位进行的划分。两类都分为输入和输出操作。
- 在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。这四个都是抽象类。
- 字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的编码来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联的。
青蛙跳台阶青蛙可以一次跳1级/2级台阶请问跳上第n级台阶有多少种方法?
- 递归:
if(target == 1){
return 1;
}
if(target == 2){
return 2;
}
//第一次有两种选择,然后根据不同的选择,然后开始不同的下一步,但是下一步还是一样有两种选择
return JumpFloor(target - 1) + JumpFloor(target - 2);
- 非递归:
int x=1, y=2, z;
if(target == 1){
return 1;
}
if(target == 2){
return 2;
}
for(int i = 3; i <= target; i ++){
z = y;
y = x + y;
x = z;
}
return y;