目录
讲讲GC过程
1.当对象1 2 3进入Eden区,进行一次垃圾回收,会把2 3 回收掉,把对象1放入S0
2.当对象4 5 6进入Eden区,进行一次垃圾回收,会把5 6 回收掉,把对象4放入S1,同时把幸存者对象1放入S1
3.当对象7 8 9进入Eden区,进行一次垃圾回收,会把8 9 回收掉,同时把幸存者对象7放入S0,对S1进行垃圾回收把幸存者4 1放入S0.如图
4.幸存者可能在S0或S1中反复移动,也可能会进行垃圾回收。
5.当他们的年龄超过15是会将他们移动到old区。
6.如果某个(些)对象(原来在年轻代中存活的对象或者新创建的对象)需要被移动到老年代中,而老年代中没有足够空间容纳这个(些)对象,那么会触发一次Full GC,Full GC会对整个堆进行清理,如果Full GC后还是无法在老年代中给这个(些)对象分配空间,那么JVM会抛出OutOfMemoryError,即OOM。
如何设置jvm参数?
分别讲讲三种创建线程池的方式
- 继承Thread类创建线程类
- 实现Runnable接口
- 通过Callable和Future创建线程
采用继承Thread类方式:
(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
(2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。
采用实现Runnable接口方式:
(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
(2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
Runnable和Callable的区别:
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
(1)创建Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。
因此把run()方法称为执行体。
(2)创建了Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
package com.thread;
public class Thread01 {
public static void main(String[] args) {
MyThread01 t1=new MyThread01();
MyThread01 t2=new MyThread01();
t1.start();
t2.start();
}
}
class MyThread01 extends Thread{
@Override
public void run() {
System.out.println("线程名:"+currentThread().getName());
}
}
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执
行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是
真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
线程的执行流程很简单,当执行代码start()时,就会执行对象中重写的void run()方法,该方法执行完成
后,线程就消亡了。
package com.thread;
public class Thread02 {
public static void main(String[] args) {
MyThread02 target=new MyThread02();
Thread t1=new Thread(target);
Thread t2=new Thread(target);
t1.start();
t2.start();
}}
class MyThread02 implements Runnable{
@Override
public void run() {
System.out.println("线程名:"+Thread.currentThread().getName());
}}
实现Callable接口
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该
Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
package com.thread;
import java.util.concurrent.*;
public class Thread03 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
Callable callable=new MyThread03();
FutureTask task1=new FutureTask(callable);
FutureTask task2=new FutureTask(callable);
new Thread(task1).start();
new Thread(task2).start();
Thread.sleep(10);//等待线程执行结束
//task.get() 获取call()的返回值。若调用时call()方法未返回,则阻塞线程等待返回值
System.out.println(task1.get());
System.out.println(task2.get());
//get的传入参数为等待时间,超时抛出超时异常;传入参数为空时,则不设超时,一直等待
System.out.println(task1.get(10L, TimeUnit.MILLISECONDS));
System.out.println(task2.get(10L, TimeUnit.MILLISECONDS));
}}
class MyThread03 implements Callable{
@Override
public Object call() throws Exception {
System.out.println("线程名:"+Thread.currentThread().getName());
return "实现callable:"+Thread.currentThread().getName();
}}
TCP四次挥手中timewait作用
(1)为实现TCP连接的可靠释放
保证最后一个ACK能到达服务器,如果服务器没有收到客户端的确认报文,它会重新进行第四次挥手,这样客户端在2MSL内能收到重发的报文,并给出回应,重置2MSL计时器(MSL是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”)
(2)为使旧的重复数据包在网络中因过期而消失
服务端发送给客户端的一些报文在传输过程中由于网络拥堵而导致严重推迟,而在它到达客户端之前服务端已经重发了该报文,并完成其任务。如果在被推迟的报文未抵达前客户端就断开了连接,随后又建立了一个与之前相同IP、Port的连接,而之前被推迟的报文在这时恰好到达,而此时此新连接非彼连接,从而会发生数据错乱,进而导致无法预知的情况。因此必须维持一段等待时间,使迟到的报文在网络中完全消失。这个时间可以时所有网络中的报文到达应该到的位置,新的连接中不会出现旧的连接的报文.